diff --git a/ACKNOWLEDGEMENTS.md b/ACKNOWLEDGEMENTS.md index e6ff98f..a46b9a0 100644 --- a/ACKNOWLEDGEMENTS.md +++ b/ACKNOWLEDGEMENTS.md @@ -12,7 +12,7 @@ MIT license (https://github.com/go-co-op/gocron/blob/main/LICENSE). Telebot. https://github.com/tucnak/telebot Copyright (c) 2015 llya Kowalewski. -MIT license (https://github.com/tucnak/telebot/blob/v2/LICENSE). +MIT license (https://github.com/tucnak/telebot/blob/v3/LICENSE). Cron. https://github.com/robfig/cron. diff --git a/bot/config.go b/bot/config.go index 2aaccdc..b861cde 100644 --- a/bot/config.go +++ b/bot/config.go @@ -8,7 +8,7 @@ import ( "time" "github.com/LucaBernstein/beancount-bot-tg/helpers" - tb "gopkg.in/tucnak/telebot.v2" + tb "gopkg.in/telebot.v3" ) func (bc *BotController) configHandler(m *tb.Message) { diff --git a/bot/config_test.go b/bot/config_test.go index a9717c8..132154f 100644 --- a/bot/config_test.go +++ b/bot/config_test.go @@ -10,7 +10,7 @@ import ( "github.com/DATA-DOG/go-sqlmock" "github.com/LucaBernstein/beancount-bot-tg/db/crud" "github.com/LucaBernstein/beancount-bot-tg/helpers" - tb "gopkg.in/tucnak/telebot.v2" + tb "gopkg.in/telebot.v3" ) func TestConfigCurrency(t *testing.T) { @@ -44,13 +44,13 @@ func TestConfigCurrency(t *testing.T) { bot := &MockBot{} bc.AddBotAndStart(bot) - bc.commandConfig(&tb.Message{Text: "/config", Chat: chat}) + bc.commandConfig(&MockContext{M: &tb.Message{Text: "/config", Chat: chat}}) if !strings.Contains(fmt.Sprintf("%v", bot.LastSentWhat), "Usage help for /config") { t.Errorf("/config: %s", bot.LastSentWhat) } // Default currency - bc.commandConfig(&tb.Message{Text: "/config currency", Chat: chat}) + bc.commandConfig(&MockContext{M: &tb.Message{Text: "/config currency", Chat: chat}}) if strings.Contains(fmt.Sprintf("%v", bot.LastSentWhat), "Usage help") { t.Errorf("/config currency: Unexpected usage help: %s", bot.LastSentWhat) } @@ -59,7 +59,7 @@ func TestConfigCurrency(t *testing.T) { } // Currency set in db - bc.commandConfig(&tb.Message{Text: "/config currency", Chat: chat}) + bc.commandConfig(&MockContext{M: &tb.Message{Text: "/config currency", Chat: chat}}) if strings.Contains(fmt.Sprintf("%v", bot.LastSentWhat), "Usage help") { t.Errorf("/config currency: Unexpected usage help: %s", bot.LastSentWhat) } @@ -67,7 +67,7 @@ func TestConfigCurrency(t *testing.T) { t.Errorf("/config currency set (2): Expected currency to be retrieved from db: %s", bot.LastSentWhat) } - bc.commandConfig(&tb.Message{Text: "/config currency USD", Chat: chat}) + bc.commandConfig(&MockContext{M: &tb.Message{Text: "/config currency USD", Chat: chat}}) if strings.Contains(fmt.Sprintf("%v", bot.LastSentWhat), "Usage help") { t.Errorf("/config currency USD: Unexpected usage help: %s", bot.LastSentWhat) } @@ -93,7 +93,7 @@ func TestConfigTag(t *testing.T) { bot := &MockBot{} bc.AddBotAndStart(bot) - bc.commandConfig(&tb.Message{Text: "/config tag invalid amount of parameters", Chat: chat}) + bc.commandConfig(&MockContext{M: &tb.Message{Text: "/config tag invalid amount of parameters", Chat: chat}}) if !strings.Contains(fmt.Sprintf("%v", bot.LastSentWhat), "Usage help for /config") { t.Errorf("/config tag invalid amount of parameters: %s", bot.LastSentWhat) } @@ -105,7 +105,7 @@ func TestConfigTag(t *testing.T) { WithArgs(12345, helpers.USERSET_TAG, "vacation2021"). WillReturnResult(sqlmock.NewResult(1, 1)) mock.ExpectCommit() - bc.commandConfig(&tb.Message{Text: "/config tag vacation2021", Chat: chat}) + bc.commandConfig(&MockContext{M: &tb.Message{Text: "/config tag vacation2021", Chat: chat}}) if strings.Contains(fmt.Sprintf("%v", bot.LastSentWhat), "Usage help for /config") { t.Errorf("/config tag vacation2021: %s", bot.LastSentWhat) } @@ -118,7 +118,7 @@ func TestConfigTag(t *testing.T) { ExpectQuery(`SELECT "value" FROM "bot::userSetting"`). WithArgs(chat.ID, helpers.USERSET_TAG). WillReturnRows(sqlmock.NewRows([]string{"value"}).AddRow("vacation2021")) - bc.commandConfig(&tb.Message{Text: "/config tag", Chat: chat}) + bc.commandConfig(&MockContext{M: &tb.Message{Text: "/config tag", Chat: chat}}) if strings.Contains(fmt.Sprintf("%v", bot.LastSentWhat), "Usage help for /config") { t.Errorf("/config tag: %s", bot.LastSentWhat) } @@ -130,7 +130,7 @@ func TestConfigTag(t *testing.T) { ExpectQuery(`SELECT "value" FROM "bot::userSetting"`). WithArgs(chat.ID, helpers.USERSET_TAG). WillReturnRows(sqlmock.NewRows([]string{"tag"}).AddRow(nil)) - bc.commandConfig(&tb.Message{Text: "/config tag", Chat: chat}) + bc.commandConfig(&MockContext{M: &tb.Message{Text: "/config tag", Chat: chat}}) if strings.Contains(fmt.Sprintf("%v", bot.LastSentWhat), "Usage help for /config") { t.Errorf("/config tag: %s", bot.LastSentWhat) } @@ -142,7 +142,7 @@ func TestConfigTag(t *testing.T) { mock.ExpectBegin() mock.ExpectExec(`DELETE FROM "bot::userSetting"`).WithArgs(12345, helpers.USERSET_TAG).WillReturnResult(sqlmock.NewResult(1, 1)) mock.ExpectCommit() - bc.commandConfig(&tb.Message{Text: "/config tag off", Chat: chat}) + bc.commandConfig(&MockContext{M: &tb.Message{Text: "/config tag off", Chat: chat}}) if strings.Contains(fmt.Sprintf("%v", bot.LastSentWhat), "Usage help for /config") { t.Errorf("/config tag off: %s", bot.LastSentWhat) } @@ -177,7 +177,7 @@ func TestConfigHandleNotification(t *testing.T) { mock.ExpectQuery(`SELECT "delayHours", "notificationHour" FROM "bot::notificationSchedule"`). WithArgs(chat.ID). WillReturnRows(sqlmock.NewRows([]string{"delayHours", "notificationHour"})) - bc.commandConfig(&tb.Message{Text: "/config notify", Chat: chat}) + bc.commandConfig(&MockContext{M: &tb.Message{Text: "/config notify", Chat: chat}}) if !strings.Contains(fmt.Sprintf("%v", bot.LastSentWhat), "Notifications are disabled for open transactions") { t.Errorf("Notifications should be disabled: %s", bot.LastSentWhat) } @@ -189,7 +189,7 @@ func TestConfigHandleNotification(t *testing.T) { mock.ExpectQuery(`SELECT "delayHours", "notificationHour" FROM "bot::notificationSchedule"`). WithArgs(chat.ID). WillReturnRows(sqlmock.NewRows([]string{"delayHours", "notificationHour"}).AddRow(24, 18)) - bc.commandConfig(&tb.Message{Text: "/config notify", Chat: chat}) + bc.commandConfig(&MockContext{M: &tb.Message{Text: "/config notify", Chat: chat}}) if !strings.Contains(fmt.Sprintf("%v", bot.LastSentWhat), "The bot will notify you daily at hour 18 ("+tz+"+3) if transactions are open for more than 1 day") { t.Errorf("Notifications should be disabled: %s", bot.LastSentWhat) @@ -199,7 +199,7 @@ func TestConfigHandleNotification(t *testing.T) { ExpectQuery(`SELECT "value" FROM "bot::userSetting"`). WithArgs(chat.ID, helpers.USERSET_TZOFF). WillReturnRows(sqlmock.NewRows([]string{"value"}).AddRow("14")) - bc.commandConfig(&tb.Message{Text: "/config notify 17", Chat: chat}) + bc.commandConfig(&MockContext{M: &tb.Message{Text: "/config notify 17", Chat: chat}}) if !strings.Contains(fmt.Sprintf("%v", bot.LastSentWhat), "invalid parameter") { t.Errorf("Single number as param should not be allowed: %s", bot.LastSentWhat) } @@ -209,7 +209,7 @@ func TestConfigHandleNotification(t *testing.T) { WithArgs(chat.ID, helpers.USERSET_TZOFF). WillReturnRows(sqlmock.NewRows([]string{"value"}).AddRow("0")) mock.ExpectExec(`DELETE FROM "bot::notificationSchedule"`).WithArgs(chat.ID).WillReturnResult(sqlmock.NewResult(1, 1)) - bc.commandConfig(&tb.Message{Text: "/config notify off", Chat: chat}) + bc.commandConfig(&MockContext{M: &tb.Message{Text: "/config notify off", Chat: chat}}) if !strings.Contains(fmt.Sprintf("%v", bot.LastSentWhat), "Successfully disabled notifications") { t.Errorf("Single param should be allowed for 'off' to disable notifications: %s", bot.LastSentWhat) } @@ -228,7 +228,7 @@ func TestConfigHandleNotification(t *testing.T) { mock.ExpectQuery(`SELECT "delayHours", "notificationHour" FROM "bot::notificationSchedule"`). WithArgs(chat.ID). WillReturnRows(sqlmock.NewRows([]string{"delayHours", "notificationHour"}).AddRow(4*24, 23)) - bc.commandConfig(&tb.Message{Text: "/config notify 4 23", Chat: chat}) + bc.commandConfig(&MockContext{M: &tb.Message{Text: "/config notify 4 23", Chat: chat}}) if !strings.Contains(fmt.Sprintf("%v", bot.LastSentWhat), "The bot will notify you daily at hour 23 ("+tz+"-2) if transactions are open for more than 4 days") { t.Errorf("Should successfully set notification: %s", bot.LastSentWhat) @@ -239,7 +239,7 @@ func TestConfigHandleNotification(t *testing.T) { ExpectQuery(`SELECT "value" FROM "bot::userSetting"`). WithArgs(chat.ID, helpers.USERSET_TZOFF). WillReturnRows(sqlmock.NewRows([]string{"value"}).AddRow("1")) - bc.commandConfig(&tb.Message{Text: "/config notify 4 24", Chat: chat}) + bc.commandConfig(&MockContext{M: &tb.Message{Text: "/config notify 4 24", Chat: chat}}) if !strings.Contains(fmt.Sprintf("%v", bot.LastSentWhat), "invalid hour (24 is out of valid range 1-23)") { t.Errorf("Out of bounds notification hour: %s", bot.LastSentWhat) @@ -259,7 +259,7 @@ func TestConfigAbout(t *testing.T) { bot := &MockBot{} bc.AddBotAndStart(bot) - bc.commandConfig(&tb.Message{Text: "/config about", Chat: chat}) + bc.commandConfig(&MockContext{M: &tb.Message{Text: "/config about", Chat: chat}}) if !strings.Contains(fmt.Sprintf("%v", bot.LastSentWhat), "LucaBernstein/beancount\\-bot\\-tg") { t.Errorf("Should contain repo link: %s", bot.LastSentWhat) diff --git a/bot/controller.go b/bot/controller.go index edfd1c2..453fad1 100644 --- a/bot/controller.go +++ b/bot/controller.go @@ -10,13 +10,13 @@ import ( "github.com/LucaBernstein/beancount-bot-tg/db/crud" "github.com/LucaBernstein/beancount-bot-tg/helpers" "github.com/go-co-op/gocron" - tb "gopkg.in/tucnak/telebot.v2" + tb "gopkg.in/telebot.v3" ) type CMD struct { CommandAlias []string Optional []string - Handler func(m *tb.Message) + Handler tb.HandlerFunc Help string } @@ -104,21 +104,22 @@ func (bc *BotController) commandMappings() []*CMD { } } -func (bc *BotController) commandStart(m *tb.Message) { - bc.Logf(TRACE, m, "Start command") - _, err := bc.Bot.Send(Recipient(m), "Welcome to this beancount bot!\n"+ +func (bc *BotController) commandStart(c tb.Context) error { + bc.Logf(TRACE, c.Message(), "Start command") + _, err := bc.Bot.Send(Recipient(c.Message()), "Welcome to this beancount bot!\n"+ "You can find more information in the repository under "+ "https://github.com/LucaBernstein/beancount-bot-tg\n\n"+ "Please check the commands I will send to you next that are available to you. "+ "You can always reach the command help by typing /"+CMD_HELP, clearKeyboard()) if err != nil { - bc.Logf(ERROR, m, "Sending bot message failed: %s", err.Error()) + bc.Logf(ERROR, c.Message(), "Sending bot message failed: %s", err.Error()) } - bc.commandHelp(m) + bc.commandHelp(c) + return nil } -func (bc *BotController) commandHelp(m *tb.Message) { - bc.Logf(TRACE, m, "Sending help") +func (bc *BotController) commandHelp(c tb.Context) error { + bc.Logf(TRACE, c.Message(), "Sending help") helpMsg := "" adminCommands := []*CMD{} for i, cmd := range bc.commandMappings() { @@ -140,24 +141,25 @@ func (bc *BotController) commandHelp(m *tb.Message) { } helpMsg += fmt.Sprintf("/%s%s - %s", cmd.CommandAlias[0], optional, cmd.Help) } - if len(adminCommands) > 0 && bc.Repo.UserIsAdmin(m) { + if len(adminCommands) > 0 && bc.Repo.UserIsAdmin(c.Message()) { helpMsg += "\n\n** ADMIN COMMANDS **" for _, cmd := range adminCommands { helpMsg += fmt.Sprintf("\n/%s - %s", cmd.CommandAlias[0], cmd.Help) } } - _, err := bc.Bot.Send(Recipient(m), helpMsg, clearKeyboard()) + _, err := bc.Bot.Send(Recipient(c.Message()), helpMsg, clearKeyboard()) if err != nil { - bc.Logf(ERROR, m, "Sending bot message failed: %s", err.Error()) + bc.Logf(ERROR, c.Message(), "Sending bot message failed: %s", err.Error()) } + return nil } -func (bc *BotController) commandCancel(m *tb.Message) { - tx := bc.State.GetType(m) +func (bc *BotController) commandCancel(c tb.Context) error { + tx := bc.State.GetType(c.Message()) hasState := tx != ST_NONE - bc.Logf(TRACE, m, "Clearing state. Had state? %t > '%s'", hasState, tx) + bc.Logf(TRACE, c.Message(), "Clearing state. Had state? %t > '%s'", hasState, tx) - bc.State.Clear(m) + bc.State.Clear(c.Message()) msg := "You did not currently have any state or transaction open that could be cancelled." if hasState { @@ -167,10 +169,11 @@ func (bc *BotController) commandCancel(m *tb.Message) { msg = "Your currently running transaction has been cancelled." } } - _, err := bc.Bot.Send(Recipient(m), fmt.Sprintf("%s\nType /%s to get available commands.", msg, CMD_HELP), clearKeyboard()) + _, err := bc.Bot.Send(Recipient(c.Message()), fmt.Sprintf("%s\nType /%s to get available commands.", msg, CMD_HELP), clearKeyboard()) if err != nil { - bc.Logf(ERROR, m, "Sending bot message failed: %s", err.Error()) + bc.Logf(ERROR, c.Message(), "Sending bot message failed: %s", err.Error()) } + return nil } const MSG_UNFINISHED_STATE = "You have an unfinished operation running. Please finish it or /cancel it before starting a new one." @@ -187,56 +190,57 @@ func Recipient(m *tb.Message) tb.Recipient { return &Sender{recipient: fmt.Sprintf("%d", m.Chat.ID)} } -func (bc *BotController) commandCreateSimpleTx(m *tb.Message) { - state := bc.State.GetType(m) +func (bc *BotController) commandCreateSimpleTx(c tb.Context) error { + state := bc.State.GetType(c.Message()) if state != ST_NONE { - _, err := bc.Bot.Send(Recipient(m), MSG_UNFINISHED_STATE) + _, err := bc.Bot.Send(Recipient(c.Message()), MSG_UNFINISHED_STATE) if err != nil { - bc.Logf(ERROR, m, "Sending bot message failed: %s", err.Error()) + bc.Logf(ERROR, c.Message(), "Sending bot message failed: %s", err.Error()) } - return + return nil } - bc.Logf(TRACE, m, "Creating simple transaction") - _, err := bc.Bot.Send(Recipient(m), "In the following steps we will create a simple transaction. "+ + bc.Logf(TRACE, c.Message(), "Creating simple transaction") + _, err := bc.Bot.Send(Recipient(c.Message()), "In the following steps we will create a simple transaction. "+ "I will guide you through.\n\n", clearKeyboard(), ) if err != nil { - bc.Logf(ERROR, m, "Sending bot message failed: %s", err.Error()) + bc.Logf(ERROR, c.Message(), "Sending bot message failed: %s", err.Error()) } - tx, err := bc.State.SimpleTx(m, bc.Repo.UserGetCurrency(m)) // create new tx + tx, err := bc.State.SimpleTx(c.Message(), bc.Repo.UserGetCurrency(c.Message())) // create new tx if err != nil { - _, err := bc.Bot.Send(Recipient(m), "Something went wrong creating your transactions ("+err.Error()+"). Please check /help for usage."+ + _, err := bc.Bot.Send(Recipient(c.Message()), "Something went wrong creating your transactions ("+err.Error()+"). Please check /help for usage."+ "\n\nYou can create a simple transaction using this command: /simple [date]\ne.g. /simple 2021-01-24\n"+ "The date parameter is non-mandatory, if not specified, today's date will be taken."+ "Alternatively it is also possible to send an amount directly to start a new simple transaction.", clearKeyboard()) if err != nil { - bc.Logf(ERROR, m, "Sending bot message failed: %s", err.Error()) + bc.Logf(ERROR, c.Message(), "Sending bot message failed: %s", err.Error()) } - return + return nil } if tx.IsDone() { - bc.finishTransaction(m, tx) - return + bc.finishTransaction(c.Message(), tx) + return nil } - hint := tx.NextHint(bc.Repo, m) - bc.sendNextTxHint(hint, m) + hint := tx.NextHint(bc.Repo, c.Message()) + bc.sendNextTxHint(hint, c.Message()) + return nil } -func (bc *BotController) commandAddComment(m *tb.Message) { - if bc.State.GetType(m) != ST_NONE { - bc.Logf(INFO, m, "commandAddComment while in another transaction") - _, err := bc.Bot.Send(Recipient(m), MSG_UNFINISHED_STATE) +func (bc *BotController) commandAddComment(c tb.Context) error { + if bc.State.GetType(c.Message()) != ST_NONE { + bc.Logf(INFO, c.Message(), "commandAddComment while in another transaction") + _, err := bc.Bot.Send(Recipient(c.Message()), MSG_UNFINISHED_STATE) if err != nil { - bc.Logf(ERROR, m, "Sending bot message failed: %s", err.Error()) + bc.Logf(ERROR, c.Message(), "Sending bot message failed: %s", err.Error()) } - return + return nil } base := CMD_COMMENT[0] - if !strings.HasPrefix(m.Text, "/"+base) { + if !strings.HasPrefix(c.Message().Text, "/"+base) { base = CMD_COMMENT[1] } - remainingCommand := strings.TrimPrefix(strings.TrimLeft(m.Text, ""), "/"+base) + remainingCommand := strings.TrimPrefix(strings.TrimLeft(c.Message().Text, ""), "/"+base) // Issue #91: Support unquoted comments comment := strings.TrimSpace(remainingCommand) @@ -245,24 +249,25 @@ func (bc *BotController) commandAddComment(m *tb.Message) { } comment = strings.ReplaceAll(comment, "\\\"", "\"") - err := bc.Repo.RecordTransaction(m.Chat.ID, comment+"\n") + err := bc.Repo.RecordTransaction(c.Message().Chat.ID, comment+"\n") if err != nil { - bc.Logf(ERROR, m, "Something went wrong while recording the comment: "+err.Error()) - _, err := bc.Bot.Send(Recipient(m), "Something went wrong while recording your comment: "+err.Error(), clearKeyboard()) + bc.Logf(ERROR, c.Message(), "Something went wrong while recording the comment: "+err.Error()) + _, err := bc.Bot.Send(Recipient(c.Message()), "Something went wrong while recording your comment: "+err.Error(), clearKeyboard()) if err != nil { - bc.Logf(ERROR, m, "Sending bot message failed: %s", err.Error()) + bc.Logf(ERROR, c.Message(), "Sending bot message failed: %s", err.Error()) } - return + return nil } - _, err = bc.Bot.Send(Recipient(m), "Successfully added the comment to your transaction /list", clearKeyboard()) + _, err = bc.Bot.Send(Recipient(c.Message()), "Successfully added the comment to your transaction /list", clearKeyboard()) if err != nil { - bc.Logf(ERROR, m, "Sending bot message failed: %s", err.Error()) + bc.Logf(ERROR, c.Message(), "Sending bot message failed: %s", err.Error()) } + return nil } -func (bc *BotController) commandList(m *tb.Message) { - bc.Logf(TRACE, m, "Listing transactions") - command := strings.Split(m.Text, " ") +func (bc *BotController) commandList(c tb.Context) error { + bc.Logf(TRACE, c.Message(), "Listing transactions") + command := strings.Split(c.Message().Text, " ") isArchived := false isDated := false isNumbered := false @@ -286,55 +291,55 @@ func (bc *BotController) commandList(m *tb.Message) { var err error elementNumber, err = strconv.Atoi(option) if err != nil { - _, err := bc.Bot.Send(Recipient(m), fmt.Sprintf("The option '%s' could not be recognized. Please try again with '/list', with options added to the end separated by space.", option), clearKeyboard()) + _, err := bc.Bot.Send(Recipient(c.Message()), fmt.Sprintf("The option '%s' could not be recognized. Please try again with '/list', with options added to the end separated by space.", option), clearKeyboard()) if err != nil { - bc.Logf(ERROR, m, "Sending bot message failed: %s", err.Error()) + bc.Logf(ERROR, c.Message(), "Sending bot message failed: %s", err.Error()) } - return + return nil } continue } } } if isDeleteCommand && (isNumbered || isDated || elementNumber <= 0) { - _, err := bc.Bot.Send(Recipient(m), "For removing a single element from the list, determine it's number by sending the command '/list numbered' and then removing an entry by sending '/list rm '.", clearKeyboard()) + _, err := bc.Bot.Send(Recipient(c.Message()), "For removing a single element from the list, determine it's number by sending the command '/list numbered' and then removing an entry by sending '/list rm '.", clearKeyboard()) if err != nil { - bc.Logf(ERROR, m, "Sending bot message failed: %s", err.Error()) + bc.Logf(ERROR, c.Message(), "Sending bot message failed: %s", err.Error()) } - return + return nil } - tx, err := bc.Repo.GetTransactions(m, isArchived) + tx, err := bc.Repo.GetTransactions(c.Message(), isArchived) if err != nil { - _, err := bc.Bot.Send(Recipient(m), "Something went wrong retrieving your transactions: "+err.Error(), clearKeyboard()) + _, err := bc.Bot.Send(Recipient(c.Message()), "Something went wrong retrieving your transactions: "+err.Error(), clearKeyboard()) if err != nil { - bc.Logf(ERROR, m, "Sending bot message failed: %s", err.Error()) + bc.Logf(ERROR, c.Message(), "Sending bot message failed: %s", err.Error()) } - return + return nil } if tx == nil { - bc.Logf(ERROR, m, "Tx unexpectedly was nil") - return + bc.Logf(ERROR, c.Message(), "Tx unexpectedly was nil") + return nil } if isDeleteCommand { var err error if elementNumber <= len(tx) { elementDbId := tx[elementNumber-1].Id - err = bc.Repo.DeleteTransaction(m, isArchived, elementDbId) + err = bc.Repo.DeleteTransaction(c.Message(), isArchived, elementDbId) } else { err = fmt.Errorf("the number you specified was too high. Please use a correct number as seen from '/list [archived] numbered'") } if err != nil { - _, errSending := bc.Bot.Send(Recipient(m), "Something went wrong while trying to delete a single transaction: "+err.Error(), clearKeyboard()) + _, errSending := bc.Bot.Send(Recipient(c.Message()), "Something went wrong while trying to delete a single transaction: "+err.Error(), clearKeyboard()) if errSending != nil { - bc.Logf(ERROR, m, "Sending bot message failed: %s", errSending.Error()) + bc.Logf(ERROR, c.Message(), "Sending bot message failed: %s", errSending.Error()) } - return + return nil } - _, errSending := bc.Bot.Send(Recipient(m), "Successfully deleted the list entry specified.", clearKeyboard()) + _, errSending := bc.Bot.Send(Recipient(c.Message()), "Successfully deleted the list entry specified.", clearKeyboard()) if errSending != nil { - bc.Logf(ERROR, m, "Sending bot message failed: %s", errSending.Error()) + bc.Logf(ERROR, c.Message(), "Sending bot message failed: %s", errSending.Error()) } - return + return nil } SEP := "\n" txList := []string{} @@ -343,13 +348,13 @@ func (bc *BotController) commandList(m *tb.Message) { var dateComment string txEntryNumber++ if isDated { - tzOffset := bc.Repo.UserGetTzOffset(m) + tzOffset := bc.Repo.UserGetTzOffset(c.Message()) timezoneOff := time.Duration(tzOffset) * time.Hour // 2022-03-30T14:24:50.390084Z dateParsed, err := time.Parse("2006-01-02T15:04:05Z", t.Date) if err != nil { - bc.Logf(ERROR, m, "Parsing time failed: %s", err.Error()) - bc.Logf(WARN, m, "Turning off dated option!") + bc.Logf(ERROR, c.Message(), "Parsing time failed: %s", err.Error()) + bc.Logf(WARN, c.Message(), "Turning off dated option!") isDated = false } else { date := dateParsed.Add(timezoneOff).Format(helpers.BEANCOUNT_DATE_FORMAT + " 15:04") @@ -369,19 +374,20 @@ func (bc *BotController) commandList(m *tb.Message) { if !isArchived { archivedSuggestion = " archived" } - _, err := bc.Bot.Send(Recipient(m), fmt.Sprintf("Your transaction list is empty. Create some first. Check /%s for commands to create a transaction."+ + _, err := bc.Bot.Send(Recipient(c.Message()), fmt.Sprintf("Your transaction list is empty. Create some first. Check /%s for commands to create a transaction."+ "\nYou might also be looking for%s transactions using '/list%s'.", CMD_HELP, archivedSuggestion, archivedSuggestion), clearKeyboard()) if err != nil { - bc.Logf(ERROR, m, "Sending bot message failed: %s", err.Error()) + bc.Logf(ERROR, c.Message(), "Sending bot message failed: %s", err.Error()) } - return + return nil } for _, message := range messageSplits { - _, err = bc.Bot.Send(Recipient(m), message, clearKeyboard()) + _, err = bc.Bot.Send(Recipient(c.Message()), message, clearKeyboard()) if err != nil { - bc.Logf(ERROR, m, "Sending bot message failed: %s", err.Error()) + bc.Logf(ERROR, c.Message(), "Sending bot message failed: %s", err.Error()) } } + return nil } func (bc *BotController) MergeMessagesHonorSendLimit(m []string, sep string) []string { @@ -404,55 +410,60 @@ func (bc *BotController) MergeMessagesHonorSendLimit(m []string, sep string) []s return messages } -func (bc *BotController) commandArchiveTransactions(m *tb.Message) { - bc.Logf(TRACE, m, "Archiving transactions") - err := bc.Repo.ArchiveTransactions(m) +func (bc *BotController) commandArchiveTransactions(c tb.Context) error { + bc.Logf(TRACE, c.Message(), "Archiving transactions") + err := bc.Repo.ArchiveTransactions(c.Message()) if err != nil { - _, err := bc.Bot.Send(Recipient(m), "Something went wrong archiving your transactions: "+err.Error()) + _, err := bc.Bot.Send(Recipient(c.Message()), "Something went wrong archiving your transactions: "+err.Error()) if err != nil { - bc.Logf(ERROR, m, "Sending bot message failed: %s", err.Error()) + bc.Logf(ERROR, c.Message(), "Sending bot message failed: %s", err.Error()) } - return + return nil } - _, err = bc.Bot.Send(Recipient(m), fmt.Sprintf("Archived all transactions. Your /%s is empty again.", CMD_LIST), clearKeyboard()) + _, err = bc.Bot.Send(Recipient(c.Message()), fmt.Sprintf("Archived all transactions. Your /%s is empty again.", CMD_LIST), clearKeyboard()) if err != nil { - bc.Logf(ERROR, m, "Sending bot message failed: %s", err.Error()) + bc.Logf(ERROR, c.Message(), "Sending bot message failed: %s", err.Error()) } + return nil } -func (bc *BotController) commandDeleteTransactions(m *tb.Message) { - if !(strings.TrimSpace(strings.ToLower(m.Text)) == strings.ToLower("/"+CMD_DELETE_ALL+" YES")) { - _, err := bc.Bot.Send(Recipient(m), fmt.Sprintf("Please type '/%s yes' to confirm the deletion of your transactions", CMD_DELETE_ALL)) +func (bc *BotController) commandDeleteTransactions(c tb.Context) error { + if !(strings.TrimSpace(strings.ToLower(c.Message().Text)) == strings.ToLower("/"+CMD_DELETE_ALL+" YES")) { + _, err := bc.Bot.Send(Recipient(c.Message()), fmt.Sprintf("Please type '/%s yes' to confirm the deletion of your transactions", CMD_DELETE_ALL)) if err != nil { - bc.Logf(ERROR, m, "Sending bot message failed: %s", err.Error()) + bc.Logf(ERROR, c.Message(), "Sending bot message failed: %s", err.Error()) } - return + return nil } - bc.Logf(TRACE, m, "Deleting transactions") - err := bc.Repo.DeleteTransactions(m) + bc.Logf(TRACE, c.Message(), "Deleting transactions") + err := bc.Repo.DeleteTransactions(c.Message()) if err != nil { - _, err := bc.Bot.Send(Recipient(m), "Something went wrong deleting your transactions: "+err.Error()) + _, err := bc.Bot.Send(Recipient(c.Message()), "Something went wrong deleting your transactions: "+err.Error()) if err != nil { - bc.Logf(ERROR, m, "Sending bot message failed: %s", err.Error()) + bc.Logf(ERROR, c.Message(), "Sending bot message failed: %s", err.Error()) } - return + return nil } - _, err = bc.Bot.Send(Recipient(m), fmt.Sprintf("Permanently deleted all your transactions. Your /%s is empty again.", CMD_LIST), clearKeyboard()) + _, err = bc.Bot.Send(Recipient(c.Message()), fmt.Sprintf("Permanently deleted all your transactions. Your /%s is empty again.", CMD_LIST), clearKeyboard()) if err != nil { - bc.Logf(ERROR, m, "Sending bot message failed: %s", err.Error()) + bc.Logf(ERROR, c.Message(), "Sending bot message failed: %s", err.Error()) } + return nil } -func (bc *BotController) commandTemplates(m *tb.Message) { - bc.templatesHandler(m) +func (bc *BotController) commandTemplates(c tb.Context) error { + bc.templatesHandler(c.Message()) + return nil } -func (bc *BotController) commandSuggestions(m *tb.Message) { - bc.suggestionsHandler(m) +func (bc *BotController) commandSuggestions(c tb.Context) error { + bc.suggestionsHandler(c.Message()) + return nil } -func (bc *BotController) commandConfig(m *tb.Message) { - bc.configHandler(m) +func (bc *BotController) commandConfig(c tb.Context) error { + bc.configHandler(c.Message()) + return nil } func (bc *BotController) cronInfo() string { @@ -512,48 +523,48 @@ func (r ReceiverImpl) Recipient() string { return r.chatId } -func (bc *BotController) commandAdminCronInfo(m *tb.Message) { - isAdmin := bc.Repo.UserIsAdmin(m) +func (bc *BotController) commandAdminCronInfo(c tb.Context) error { + isAdmin := bc.Repo.UserIsAdmin(c.Message()) if !isAdmin { - bc.Logf(WARN, m, "Received admin command from non-admin user. Ignoring (treating as normal text input).") - bc.handleTextState(m) - return + bc.Logf(WARN, c.Message(), "Received admin command from non-admin user. Ignoring (treating as normal text input).") + bc.handleTextState(c) + return nil } - _, err := bc.Bot.Send(Recipient(m), bc.cronInfo()) + _, err := bc.Bot.Send(Recipient(c.Message()), bc.cronInfo()) if err != nil { - bc.Logf(ERROR, m, "Sending bot message failed: %s", err.Error()) + bc.Logf(ERROR, c.Message(), "Sending bot message failed: %s", err.Error()) } + return nil } -func (bc *BotController) commandAdminNofify(m *tb.Message) { - isAdmin := bc.Repo.UserIsAdmin(m) +func (bc *BotController) commandAdminNofify(c tb.Context) error { + isAdmin := bc.Repo.UserIsAdmin(c.Message()) if !isAdmin { - bc.Logf(WARN, m, "Received admin command from non-admin user. Ignoring (treating as normal text input).") - bc.handleTextState(m) - return + bc.Logf(WARN, c.Message(), "Received admin command from non-admin user. Ignoring (treating as normal text input).") + return bc.handleTextState(c) } - text := strings.Split(m.Text, "\"") + text := strings.Split(c.Message().Text, "\"") var notificationMessage string if len(text) >= 2 { notificationMessage = text[1] } if len(text) == 0 || len(notificationMessage) == 0 { - _, err := bc.Bot.Send(Recipient(m), "Something went wrong splitting your command parameters. Did you specify a text in double quotes (\")?") + _, err := bc.Bot.Send(Recipient(c.Message()), "Something went wrong splitting your command parameters. Did you specify a text in double quotes (\")?") if err != nil { - bc.Logf(ERROR, m, "Sending bot message failed: %s", err.Error()) + bc.Logf(ERROR, c.Message(), "Sending bot message failed: %s", err.Error()) } - return + return nil } // text[0] = /command [chatId] command := strings.Split(strings.TrimRight(text[0], " "), " ") if len(command) == 0 || len(command) >= 3 { // invalid argument count - _, err := bc.Bot.Send(Recipient(m), "Please check the command syntax") + _, err := bc.Bot.Send(Recipient(c.Message()), "Please check the command syntax") if err != nil { - bc.Logf(ERROR, m, "Sending bot message failed: %s", err.Error()) + bc.Logf(ERROR, c.Message(), "Sending bot message failed: %s", err.Error()) } - return + return nil } var target string @@ -563,43 +574,44 @@ func (bc *BotController) commandAdminNofify(m *tb.Message) { receivers := bc.Repo.IndividualsWithNotifications(target) if len(receivers) == 0 { - _, err := bc.Bot.Send(Recipient(m), "No receivers found to send notification to (you being excluded).") + _, err := bc.Bot.Send(Recipient(c.Message()), "No receivers found to send notification to (you being excluded).") if err != nil { - bc.Logf(ERROR, m, "Sending bot message failed: %s", err.Error()) + bc.Logf(ERROR, c.Message(), "Sending bot message failed: %s", err.Error()) } - return + return nil } for _, recipient := range receivers { _, err := bc.Bot.Send(ReceiverImpl{chatId: recipient}, "*** Service notification ***\n\n"+notificationMessage) if err != nil { - bc.Logf(ERROR, m, "Sending bot message failed: %s", err.Error()) + bc.Logf(ERROR, c.Message(), "Sending bot message failed: %s", err.Error()) } - bc.Logf(TRACE, m, "Sent notification to %s", recipient) + bc.Logf(TRACE, c.Message(), "Sent notification to %s", recipient) // TODO: Add message like 'If you don't want to receive further service notifications, you can turn them off in the /settings with '/settings notif off'.' // GitHub-issue: #28 } + return nil } -func (bc *BotController) handleTextState(m *tb.Message) { - state := bc.State.GetType(m) +func (bc *BotController) handleTextState(c tb.Context) error { + state := bc.State.GetType(c.Message()) if state == ST_NONE { - if _, err := HandleFloat(m); err == nil { // Not in tx, but input would suffice for correct parsing of amount field of new tx - bc.Logf(DEBUG, m, "Creating new simple transaction as amount has been entered though not in tx") - _, err = bc.State.SimpleTx(m, bc.Repo.UserGetCurrency(m)) // create new tx + if _, err := HandleFloat(c.Message()); err == nil { // Not in tx, but input would suffice for correct parsing of amount field of new tx + bc.Logf(DEBUG, c.Message(), "Creating new simple transaction as amount has been entered though not in tx") + _, err = bc.State.SimpleTx(c.Message(), bc.Repo.UserGetCurrency(c.Message())) // create new tx if err != nil { - _, err := bc.Bot.Send(Recipient(m), "Something went wrong creating a new transaction: "+err.Error(), clearKeyboard()) + _, err := bc.Bot.Send(Recipient(c.Message()), "Something went wrong creating a new transaction: "+err.Error(), clearKeyboard()) if err != nil { - bc.Logf(ERROR, m, "Sending bot message failed: %s", err.Error()) + bc.Logf(ERROR, c.Message(), "Sending bot message failed: %s", err.Error()) } - return + return nil } - _, err := bc.Bot.Send(Recipient(m), "Automatically created a new transaction for you. If you think this was a mistake you can /cancel it.", clearKeyboard()) + _, err := bc.Bot.Send(Recipient(c.Message()), "Automatically created a new transaction for you. If you think this was a mistake you can /cancel it.", clearKeyboard()) if err != nil { - bc.Logf(ERROR, m, "Sending bot message failed: %s", err.Error()) + bc.Logf(ERROR, c.Message(), "Sending bot message failed: %s", err.Error()) } - bc.handleTextState(m) - return + bc.handleTextState(c) + return nil } // If number has been entered @@ -607,41 +619,42 @@ func (bc *BotController) handleTextState(m *tb.Message) { // return // else: warn - bc.Logf(WARN, m, "Received text without having any prior state") - _, err := bc.Bot.Send(Recipient(m), fmt.Sprintf("Please check /%s on how to use this bot. E.g. you might need to start a transaction first before sending data.", CMD_HELP), clearKeyboard()) + bc.Logf(WARN, c.Message(), "Received text without having any prior state") + _, err := bc.Bot.Send(Recipient(c.Message()), fmt.Sprintf("Please check /%s on how to use this bot. E.g. you might need to start a transaction first before sending data.", CMD_HELP), clearKeyboard()) if err != nil { - bc.Logf(ERROR, m, "Sending bot message failed: %s", err.Error()) + bc.Logf(ERROR, c.Message(), "Sending bot message failed: %s", err.Error()) } - return + return nil } else if state == ST_TX { - tx := bc.State.GetTx(m) - _, err := tx.Input(m) + tx := bc.State.GetTx(c.Message()) + _, err := tx.Input(c.Message()) if err != nil { - bc.Logf(WARN, m, "Invalid text state input: '%s'. Err: %s", m.Text, err.Error()) - _, err := bc.Bot.Send(Recipient(m), "Your last input seems to have not worked.\n"+ + bc.Logf(WARN, c.Message(), "Invalid text state input: '%s'. Err: %s", c.Message().Text, err.Error()) + _, err := bc.Bot.Send(Recipient(c.Message()), "Your last input seems to have not worked.\n"+ fmt.Sprintf("(Error: %s)\n", err.Error())+ "Please try again.", ) if err != nil { - bc.Logf(ERROR, m, "Sending bot message failed: %s", err.Error()) + bc.Logf(ERROR, c.Message(), "Sending bot message failed: %s", err.Error()) } } - bc.Logf(TRACE, m, "New data state is %v. (Last input was '%s')", tx.Debug(), m.Text) + bc.Logf(TRACE, c.Message(), "New data state is %v. (Last input was '%s')", tx.Debug(), c.Message().Text) if tx.IsDone() { - bc.finishTransaction(m, tx) - return + bc.finishTransaction(c.Message(), tx) + return nil } - hint := tx.NextHint(bc.Repo, m) - bc.sendNextTxHint(hint, m) - return + hint := tx.NextHint(bc.Repo, c.Message()) + bc.sendNextTxHint(hint, c.Message()) + return nil } else if state == ST_TPL { - if bc.processNewTemplateResponse(m, bc.State.tplStates[chatId(m.Chat.ID)]) { - bc.State.Clear(m) + if bc.processNewTemplateResponse(c.Message(), bc.State.tplStates[chatId(c.Message().Chat.ID)]) { + bc.State.Clear(c.Message()) } - return + return nil } - bc.Logf(ERROR, m, "Something went wrong processing text input. Ran to end, though should have been caught by a branch. "+ + bc.Logf(ERROR, c.Message(), "Something went wrong processing text input. Ran to end, though should have been caught by a branch. "+ "Are there new state types not maintained yet?") + return nil } func (bc *BotController) sendNextTxHint(hint *Hint, m *tb.Message) { @@ -654,7 +667,7 @@ func (bc *BotController) sendNextTxHint(hint *Hint, m *tb.Message) { } func clearKeyboard() *tb.ReplyMarkup { - return &tb.ReplyMarkup{ReplyKeyboardRemove: true} + return &tb.ReplyMarkup{RemoveKeyboard: true} } func (bc *BotController) finishTransaction(m *tb.Message, tx Tx) { diff --git a/bot/controller_test.go b/bot/controller_test.go index 563a0dd..b385b5c 100644 --- a/bot/controller_test.go +++ b/bot/controller_test.go @@ -7,34 +7,14 @@ import ( "testing" "time" - tb "gopkg.in/tucnak/telebot.v2" + tb "gopkg.in/telebot.v3" "github.com/DATA-DOG/go-sqlmock" "github.com/LucaBernstein/beancount-bot-tg/db/crud" "github.com/LucaBernstein/beancount-bot-tg/helpers" ) -type MockBot struct { - LastSentWhat interface{} - AllLastSentWhat []interface{} -} -func (b *MockBot) Start() {} -func (b *MockBot) Handle(endpoint interface{}, handler interface{}) {} -func (b *MockBot) Send(to tb.Recipient, what interface{}, options ...interface{}) (*tb.Message, error) { - b.LastSentWhat = what - b.AllLastSentWhat = append(b.AllLastSentWhat, what) - return nil, nil -} -func (b *MockBot) Respond(c *tb.Callback, resp ...*tb.CallbackResponse) error { - return nil -} -func (b *MockBot) Me() *tb.User { - return &tb.User{Username: "Test bot"} -} -func (b *MockBot) reset() { - b.AllLastSentWhat = nil -} // GitHub-Issue #16: Panic if plain message without state arrives func TestTextHandlingWithoutPriorState(t *testing.T) { @@ -76,15 +56,15 @@ func TestTextHandlingWithoutPriorState(t *testing.T) { bc.AddBotAndStart(bot) // Create simple tx and fill it completely - bc.commandCreateSimpleTx(&tb.Message{Chat: chat}) + bc.commandCreateSimpleTx(&MockContext{M: &tb.Message{Chat: chat}}) tx := bc.State.txStates[12345] - tx.Input(&tb.Message{Text: "17.34"}) // amount - tx.Input(&tb.Message{Text: "Buy something in the grocery store"}) // description - tx.Input(&tb.Message{Text: "Assets:Wallet"}) // from - bc.handleTextState(&tb.Message{Chat: chat, Text: "Expenses:Groceries"}) // to (via handleTextState) + tx.Input(&tb.Message{Text: "17.34"}) // amount + tx.Input(&tb.Message{Text: "Buy something in the grocery store"}) // description + tx.Input(&tb.Message{Text: "Assets:Wallet"}) // from + bc.handleTextState(&MockContext{M: &tb.Message{Chat: chat, Text: "Expenses:Groceries"}}) // to (via handleTextState) // After the first tx is done, send some command - m := &tb.Message{Chat: chat} + m := &MockContext{M: &tb.Message{Chat: chat}} bc.handleTextState(m) // should catch and send help instead of fail @@ -115,7 +95,7 @@ func TestStartTransactionWithPlainAmountThousandsSeparated(t *testing.T) { WithArgs(chat.ID, helpers.USERSET_CUR). WillReturnRows(sqlmock.NewRows([]string{"value"}).AddRow("TEST_CURRENCY")) - bc.handleTextState(&tb.Message{Chat: chat, Text: "1,000,000"}) + bc.handleTextState(&MockContext{M: &tb.Message{Chat: chat, Text: "1,000,000"}}) debugString := bc.State.txStates[12345].Debug() expected := "data=map[amount::${SPACE_FORMAT}1000000.00" @@ -142,12 +122,12 @@ func TestTransactionDeletion(t *testing.T) { bot := &MockBot{} bc.AddBotAndStart(bot) - bc.commandDeleteTransactions(&tb.Message{Chat: chat, Text: "/deleteAll"}) + bc.commandDeleteTransactions(&MockContext{M: &tb.Message{Chat: chat, Text: "/deleteAll"}}) if !strings.Contains(fmt.Sprintf("%v", bot.LastSentWhat), "to confirm the deletion of your transactions") { t.Errorf("Deletion should require 'yes' confirmation. Got: %s", bot.LastSentWhat) } - bc.commandDeleteTransactions(&tb.Message{Chat: chat, Text: "/deleteAll YeS"}) + bc.commandDeleteTransactions(&MockContext{M: &tb.Message{Chat: chat, Text: "/deleteAll YeS"}}) if !strings.Contains(fmt.Sprintf("%v", bot.LastSentWhat), "Permanently deleted all your transactions") { t.Errorf("Deletion should work with confirmation. Got: %s", bot.LastSentWhat) } @@ -185,7 +165,7 @@ func TestTransactionListMaxLength(t *testing.T) { bc.AddBotAndStart(bot) // < 4096 chars tx - bc.commandList(&tb.Message{Chat: chat}) + bc.commandList(&MockContext{M: &tb.Message{Chat: chat}}) if len(bot.AllLastSentWhat) != 1 { t.Errorf("Expected exactly one message to be sent out: %v", bot.AllLastSentWhat) } @@ -193,7 +173,7 @@ func TestTransactionListMaxLength(t *testing.T) { bot.reset() // > 4096 chars tx - bc.commandList(&tb.Message{Chat: chat}) + bc.commandList(&MockContext{M: &tb.Message{Chat: chat}}) if len(bot.AllLastSentWhat) != 2 { t.Errorf("Expected exactly two messages to be sent out: %v", strings.Join(stringArr(bot.AllLastSentWhat), ", ")) } @@ -227,7 +207,7 @@ func TestTransactionsListArchivedDated(t *testing.T) { ) mock.ExpectQuery(`SELECT "value" FROM "bot::userSetting"`).WithArgs(12345, helpers.USERSET_TZOFF).WillReturnRows(mock.NewRows([]string{"value"})) - bc.commandList(&tb.Message{Chat: chat, Text: "/testListCommand(ignored) archived dated"}) + bc.commandList(&MockContext{M: &tb.Message{Chat: chat, Text: "/testListCommand(ignored) archived dated"}}) if bot.LastSentWhat != "; recorded on 2022-03-30 14:24\ntx1\n; recorded on 2022-03-30 15:24\ntx2" { t.Errorf("Expected last message to contain transactions:\n%v", bot.LastSentWhat) @@ -242,7 +222,7 @@ func TestTransactionsListArchivedDated(t *testing.T) { ) mock.ExpectQuery(`SELECT "value" FROM "bot::userSetting"`).WithArgs(12345, helpers.USERSET_TZOFF).WillReturnRows(mock.NewRows([]string{"value"})) - bc.commandList(&tb.Message{Chat: chat, Text: "/testListCommand(ignored) archived dated"}) + bc.commandList(&MockContext{M: &tb.Message{Chat: chat, Text: "/testListCommand(ignored) archived dated"}}) if bot.LastSentWhat != "tx1\ntx2" { t.Errorf("Expected last message to contain transactions:\n%v", bot.LastSentWhat) @@ -270,7 +250,7 @@ func TestWritingComment(t *testing.T) { bot := &MockBot{} bc.AddBotAndStart(bot) - bc.commandAddComment(&tb.Message{Chat: chat, Text: "/comment \"; This is a comment\""}) + bc.commandAddComment(&MockContext{M: &tb.Message{Chat: chat, Text: "/comment \"; This is a comment\""}}) if !strings.Contains(fmt.Sprintf("%v", bot.LastSentWhat), "added the comment") { t.Errorf("Adding comment should have worked. Got message: %s", bot.LastSentWhat) } @@ -281,7 +261,7 @@ func TestWritingComment(t *testing.T) { WithArgs(chat.ID, "This is another comment without \" (quotes)"+"\n"). WillReturnResult(sqlmock.NewResult(1, 1)) - bc.commandAddComment(&tb.Message{Chat: chat, Text: "/c This is another comment without \\\" (quotes)"}) + bc.commandAddComment(&MockContext{M: &tb.Message{Chat: chat, Text: "/c This is another comment without \\\" (quotes)"}}) if !strings.Contains(fmt.Sprintf("%v", bot.LastSentWhat), "added the comment") { t.Errorf("Adding comment should have worked. Got message: %s", bot.LastSentWhat) } @@ -315,7 +295,7 @@ func TestCommandStartHelp(t *testing.T) { ExpectQuery(`SELECT "value" FROM "bot::userSetting"`). WithArgs(chat.ID, helpers.USERSET_ADM). WillReturnRows(sqlmock.NewRows([]string{"value"}).AddRow(false)) - bc.commandStart(&tb.Message{Chat: chat}) + bc.commandStart(&MockContext{M: &tb.Message{Chat: chat}}) if !strings.Contains(fmt.Sprintf("%v", bot.AllLastSentWhat[0]), "Welcome") { t.Errorf("Bot should welcome user first") @@ -332,7 +312,7 @@ func TestCommandStartHelp(t *testing.T) { ExpectQuery(`SELECT "value" FROM "bot::userSetting"`). WithArgs(chat.ID, helpers.USERSET_ADM). WillReturnRows(sqlmock.NewRows([]string{"value"}).AddRow(true)) - bc.commandHelp(&tb.Message{Chat: chat}) + bc.commandHelp(&MockContext{M: &tb.Message{Chat: chat}}) if !strings.Contains(fmt.Sprintf("%v", bot.LastSentWhat), "admin_") { t.Errorf("Bot should send admin commands in help message for admin user") } @@ -347,7 +327,7 @@ func TestCommandCancel(t *testing.T) { bc := NewBotController(nil) bot := &MockBot{} bc.AddBotAndStart(bot) - bc.commandCancel(&tb.Message{Chat: chat}) + bc.commandCancel(&MockContext{M: &tb.Message{Chat: chat}}) if !strings.Contains(fmt.Sprintf("%v", bot.LastSentWhat), "did not currently have any state or transaction open that could be cancelled") { t.Errorf("Unexpectedly there were open tx before") } @@ -392,15 +372,15 @@ func TestTimezoneOffsetForAutomaticDate(t *testing.T) { bc.AddBotAndStart(bot) // Create simple tx and fill it completely - bc.commandCreateSimpleTx(&tb.Message{Chat: chat}) + bc.commandCreateSimpleTx(&MockContext{M: &tb.Message{Chat: chat}}) tx := bc.State.txStates[12345] - tx.Input(&tb.Message{Text: "17.34"}) // amount - tx.Input(&tb.Message{Text: "Buy something in the grocery store"}) // description - tx.Input(&tb.Message{Text: "Assets:Wallet"}) // from - bc.handleTextState(&tb.Message{Chat: chat, Text: "Expenses:Groceries"}) // to (via handleTextState) + tx.Input(&tb.Message{Text: "17.34"}) // amount + tx.Input(&tb.Message{Text: "Buy something in the grocery store"}) // description + tx.Input(&tb.Message{Text: "Assets:Wallet"}) // from + bc.handleTextState(&MockContext{&tb.Message{Chat: chat, Text: "Expenses:Groceries"}}) // to (via handleTextState) // After the first tx is done, send some command - m := &tb.Message{Chat: chat} + m := &MockContext{M: &tb.Message{Chat: chat}} bc.handleTextState(m) // should catch and send help instead of fail diff --git a/bot/handler.go b/bot/handler.go index f443a4c..ab26e0f 100644 --- a/bot/handler.go +++ b/bot/handler.go @@ -5,7 +5,7 @@ import ( "time" "github.com/LucaBernstein/beancount-bot-tg/helpers" - tb "gopkg.in/tucnak/telebot.v2" + tb "gopkg.in/telebot.v3" ) func CreateBot(bc *BotController) IBot { @@ -31,9 +31,9 @@ func CreateBot(bc *BotController) IBot { }) b, err := tb.NewBot(tb.Settings{ - Token: botToken, - Poller: userGuardPoller, - Reporter: func(e error) { bc.Logf(WARN, nil, "%s", e.Error()) }, + Token: botToken, + Poller: userGuardPoller, + OnError: func(e error, context tb.Context) { bc.Logf(WARN, nil, "%s - context: %v", e.Error(), context) }, }) if err != nil { log.Fatal(err) diff --git a/bot/logger.go b/bot/logger.go index e36cdcd..499002e 100644 --- a/bot/logger.go +++ b/bot/logger.go @@ -3,7 +3,7 @@ package bot import ( "github.com/LucaBernstein/beancount-bot-tg/db/crud" "github.com/LucaBernstein/beancount-bot-tg/helpers" - tb "gopkg.in/tucnak/telebot.v2" + tb "gopkg.in/telebot.v3" ) const ( diff --git a/bot/mocks_test.go b/bot/mocks_test.go new file mode 100644 index 0000000..d0196ed --- /dev/null +++ b/bot/mocks_test.go @@ -0,0 +1,73 @@ +package bot + +import ( + "time" + + tb "gopkg.in/telebot.v3" +) + +type MockBot struct { + LastSentWhat interface{} + AllLastSentWhat []interface{} +} + +func (b *MockBot) Start() {} +func (b *MockBot) Handle(endpoint interface{}, handler tb.HandlerFunc, mw ...tb.MiddlewareFunc) {} +func (b *MockBot) Send(to tb.Recipient, what interface{}, options ...interface{}) (*tb.Message, error) { + b.LastSentWhat = what + b.AllLastSentWhat = append(b.AllLastSentWhat, what) + return nil, nil +} +func (b *MockBot) Respond(c *tb.Callback, resp ...*tb.CallbackResponse) error { + return nil +} +func (b *MockBot) Me() *tb.User { + return &tb.User{Username: "Test bot"} +} +func (b *MockBot) reset() { + b.AllLastSentWhat = nil +} + +type MockContext struct { + M *tb.Message +} + +func (c *MockContext) Message() *tb.Message { + return c.M +} +func (c *MockContext) Bot() *tb.Bot {return nil} +func (c *MockContext) Update() tb.Update {return tb.Update{}} +func (c *MockContext) Callback() *tb.Callback {return nil} +func (c *MockContext) Query() *tb.Query {return nil} +func (c *MockContext) InlineResult() *tb.InlineResult {return nil} +func (c *MockContext) ShippingQuery() *tb.ShippingQuery {return nil} +func (c *MockContext) PreCheckoutQuery() *tb.PreCheckoutQuery {return nil} +func (c *MockContext) Poll() *tb.Poll {return nil} +func (c *MockContext) PollAnswer() *tb.PollAnswer {return nil} +func (c *MockContext) ChatMember() *tb.ChatMemberUpdate {return nil} +func (c *MockContext) ChatJoinRequest() *tb.ChatJoinRequest {return nil} +func (c *MockContext) Migration() (int64, int64) {return 0, 0} +func (c *MockContext) Sender() *tb.User {return nil} +func (c *MockContext) Chat() *tb.Chat {return nil} +func (c *MockContext) Recipient() tb.Recipient {return nil} +func (c *MockContext) Text() string {return ""} +func (c *MockContext) Data() string {return ""} +func (c *MockContext) Args() []string {return nil} +func (c *MockContext) Send(what interface{}, opts ...interface{}) error {return nil} +func (c *MockContext) SendAlbum(a tb.Album, opts ...interface{}) error {return nil} +func (c *MockContext) Reply(what interface{}, opts ...interface{}) error {return nil} +func (c *MockContext) Forward(msg tb.Editable, opts ...interface{}) error {return nil} +func (c *MockContext) ForwardTo(to tb.Recipient, opts ...interface{}) error {return nil} +func (c *MockContext) Edit(what interface{}, opts ...interface{}) error {return nil} +func (c *MockContext) EditCaption(caption string, opts ...interface{}) error {return nil} +func (c *MockContext) EditOrSend(what interface{}, opts ...interface{}) error {return nil} +func (c *MockContext) EditOrReply(what interface{}, opts ...interface{}) error {return nil} +func (c *MockContext) Delete() error {return nil} +func (c *MockContext) DeleteAfter(d time.Duration) *time.Timer {return nil} +func (c *MockContext) Notify(action tb.ChatAction) error {return nil} +func (c *MockContext) Ship(what ...interface{}) error {return nil} +func (c *MockContext) Accept(errorMessage ...string) error {return nil} +func (c *MockContext) Answer(resp *tb.QueryResponse) error {return nil} +func (c *MockContext) Respond(resp ...*tb.CallbackResponse) error {return nil} +func (c *MockContext) Get(key string) interface{} {return nil} +func (c *MockContext) Set(key string, val interface{}) {} diff --git a/bot/replyKeyboards.go b/bot/replyKeyboards.go index ce3fef0..e4c540c 100644 --- a/bot/replyKeyboards.go +++ b/bot/replyKeyboards.go @@ -1,14 +1,14 @@ package bot import ( - tb "gopkg.in/tucnak/telebot.v2" + tb "gopkg.in/telebot.v3" ) func ReplyKeyboard(buttons []string) *tb.ReplyMarkup { if len(buttons) == 0 { return clearKeyboard() } - kb := &tb.ReplyMarkup{ResizeReplyKeyboard: true, OneTimeKeyboard: true} + kb := &tb.ReplyMarkup{ResizeKeyboard: true, OneTimeKeyboard: true} buttonsCreated := []tb.Row{} for _, label := range buttons { buttonsCreated = append(buttonsCreated, kb.Row(kb.Text(label))) diff --git a/bot/stateHandler.go b/bot/stateHandler.go index 47f8a54..8b047b1 100644 --- a/bot/stateHandler.go +++ b/bot/stateHandler.go @@ -3,7 +3,7 @@ package bot import ( "strings" - tb "gopkg.in/tucnak/telebot.v2" + tb "gopkg.in/telebot.v3" ) type chatId int64 diff --git a/bot/stateHandler_test.go b/bot/stateHandler_test.go index 8ba6aca..0ed2b4e 100644 --- a/bot/stateHandler_test.go +++ b/bot/stateHandler_test.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/LucaBernstein/beancount-bot-tg/bot" - tb "gopkg.in/tucnak/telebot.v2" + tb "gopkg.in/telebot.v3" ) func TestStateClearing(t *testing.T) { diff --git a/bot/suggestions.go b/bot/suggestions.go index befbf63..78fb265 100644 --- a/bot/suggestions.go +++ b/bot/suggestions.go @@ -5,7 +5,7 @@ import ( "strings" h "github.com/LucaBernstein/beancount-bot-tg/helpers" - tb "gopkg.in/tucnak/telebot.v2" + tb "gopkg.in/telebot.v3" ) func isAllowedSuggestionType(s string) bool { diff --git a/bot/suggestions_test.go b/bot/suggestions_test.go index 8322f61..f412a73 100644 --- a/bot/suggestions_test.go +++ b/bot/suggestions_test.go @@ -7,7 +7,7 @@ import ( "testing" "github.com/DATA-DOG/go-sqlmock" - tb "gopkg.in/tucnak/telebot.v2" + tb "gopkg.in/telebot.v3" ) func TestSuggestionsHandlingWithSpaces(t *testing.T) { @@ -32,35 +32,35 @@ func TestSuggestionsHandlingWithSpaces(t *testing.T) { bc.AddBotAndStart(bot) // missing subcommand - bc.commandSuggestions(&tb.Message{Text: "/suggestions", Chat: chat}) + bc.commandSuggestions(&MockContext{M: &tb.Message{Text: "/suggestions", Chat: chat}}) if !strings.Contains(fmt.Sprintf("%v", bot.LastSentWhat), "Usage help") { t.Errorf("MissingType: Bot unexpectedly did not send usage help: %s", bot.LastSentWhat) } log.Print(1) // missing type - bc.commandSuggestions(&tb.Message{Text: "/suggestions rm", Chat: chat}) + bc.commandSuggestions(&MockContext{M: &tb.Message{Text: "/suggestions rm", Chat: chat}}) if !strings.Contains(fmt.Sprintf("%v", bot.LastSentWhat), "Usage help") { t.Errorf("MissingType: Bot unexpectedly did not send usage help: %s", bot.LastSentWhat) } - bc.commandSuggestions(&tb.Message{Text: "/suggestions rm description Too Many arguments with spaces", Chat: chat}) + bc.commandSuggestions(&MockContext{M: &tb.Message{Text: "/suggestions rm description Too Many arguments with spaces", Chat: chat}}) if !strings.Contains(fmt.Sprintf("%v", bot.LastSentWhat), "Usage help") { t.Errorf("TooManyArgs: Bot unexpectedly did not send usage help: %s", bot.LastSentWhat) } - bc.commandSuggestions(&tb.Message{Text: "/suggestions rm description \"Some description with spaces\"", Chat: chat}) + bc.commandSuggestions(&MockContext{M: &tb.Message{Text: "/suggestions rm description \"Some description with spaces\"", Chat: chat}}) if strings.Contains(fmt.Sprintf("%v", bot.LastSentWhat), "Usage help") { t.Errorf("Spaced: Bot unexpectedly sent usage help instead of performing command: %s", bot.LastSentWhat) } - bc.commandSuggestions(&tb.Message{Text: "/suggestions rm description \"SomeDescriptionWithoutSpaces\"", Chat: chat}) + bc.commandSuggestions(&MockContext{M: &tb.Message{Text: "/suggestions rm description \"SomeDescriptionWithoutSpaces\"", Chat: chat}}) if strings.Contains(fmt.Sprintf("%v", bot.LastSentWhat), "Usage help") { t.Errorf("NotSpaced: Bot unexpectedly sent usage help instead of performing command: %s", bot.LastSentWhat) } // Add is missing required value - bc.commandSuggestions(&tb.Message{Text: "/suggestions add description ", Chat: chat}) + bc.commandSuggestions(&MockContext{M: &tb.Message{Text: "/suggestions add description ", Chat: chat}}) if !strings.Contains(fmt.Sprintf("%v", bot.LastSentWhat), "Usage help") { t.Errorf("AddMissingValue: Bot did not send error: %s", bot.LastSentWhat) } diff --git a/bot/templates.go b/bot/templates.go index 7373ed7..c82cb84 100644 --- a/bot/templates.go +++ b/bot/templates.go @@ -5,7 +5,7 @@ import ( "strings" h "github.com/LucaBernstein/beancount-bot-tg/helpers" - tb "gopkg.in/tucnak/telebot.v2" + tb "gopkg.in/telebot.v3" ) func (bc *BotController) templatesHandler(m *tb.Message) { diff --git a/bot/templates_test.go b/bot/templates_test.go index 264d430..adc10d7 100644 --- a/bot/templates_test.go +++ b/bot/templates_test.go @@ -10,7 +10,7 @@ import ( "github.com/DATA-DOG/go-sqlmock" "github.com/LucaBernstein/beancount-bot-tg/db/crud" "github.com/LucaBernstein/beancount-bot-tg/helpers" - tb "gopkg.in/tucnak/telebot.v2" + tb "gopkg.in/telebot.v3" ) func TestTemplateHelpForNonexistingTemplate(t *testing.T) { @@ -30,13 +30,13 @@ func TestTemplateHelpForNonexistingTemplate(t *testing.T) { WithArgs(12345, "notexist%"). WillReturnRows(sqlmock.NewRows([]string{"name", "template"})) - bc.commandTemplates(&tb.Message{Chat: chat, Text: "/t notexist"}) + bc.commandTemplates(&MockContext{M: &tb.Message{Chat: chat, Text: "/t notexist"}}) helpers.TestStringContains(t, fmt.Sprintf("%v", bot.LastSentWhat), "Usage help for /template", "send help for invalid command") - bc.commandTemplates(&tb.Message{Chat: chat, Text: "/t add nonesense"}) - bc.handleTextState(&tb.Message{Chat: chat, Text: "Blah"}) + bc.commandTemplates(&MockContext{M: &tb.Message{Chat: chat, Text: "/t add nonesense"}}) + bc.handleTextState(&MockContext{M: &tb.Message{Chat: chat, Text: "Blah"}}) - bc.commandTemplates(&tb.Message{Chat: chat, Text: "/t"}) + bc.commandTemplates(&MockContext{M: &tb.Message{Chat: chat, Text: "/t"}}) helpers.TestStringContains(t, fmt.Sprintf("%v", bot.LastSentWhat), "Usage help for /template", "send help for invalid command") if err := mock.ExpectationsWereMet(); err != nil { @@ -61,7 +61,7 @@ func TestTemplateList(t *testing.T) { WithArgs(12345). WillReturnRows(sqlmock.NewRows([]string{"name", "template"})) - bc.commandTemplates(&tb.Message{Chat: chat, Text: "/t list"}) + bc.commandTemplates(&MockContext{M: &tb.Message{Chat: chat, Text: "/t list"}}) helpers.TestStringContains(t, fmt.Sprintf("%v", bot.LastSentWhat), "not created any template yet", "no template yet message") mock. @@ -69,7 +69,7 @@ func TestTemplateList(t *testing.T) { WithArgs(12345). WillReturnRows(sqlmock.NewRows([]string{"name", "template"}).AddRow("test1", "tpl1").AddRow("test2", "tpl2")) - bc.commandTemplates(&tb.Message{Chat: chat, Text: "/t list"}) + bc.commandTemplates(&MockContext{M: &tb.Message{Chat: chat, Text: "/t list"}}) helpers.TestStringContains(t, fmt.Sprintf("%v", bot.LastSentWhat), `test1: tpl1 @@ -81,7 +81,7 @@ tpl2`, "templates") WithArgs(12345, "test3%"). WillReturnRows(sqlmock.NewRows([]string{"name", "template"})) - bc.commandTemplates(&tb.Message{Chat: chat, Text: "/t list test3"}) + bc.commandTemplates(&MockContext{M: &tb.Message{Chat: chat, Text: "/t list test3"}}) helpers.TestStringContains(t, fmt.Sprintf("%v", bot.LastSentWhat), "No template name matched your query 'test3'", "single template response") mock. @@ -89,7 +89,7 @@ tpl2`, "templates") WithArgs(12345, "test2%"). WillReturnRows(sqlmock.NewRows([]string{"name", "template"}).AddRow("test2", "tpl2")) - bc.commandTemplates(&tb.Message{Chat: chat, Text: "/t list test2"}) + bc.commandTemplates(&MockContext{M: &tb.Message{Chat: chat, Text: "/t list test2"}}) helpers.TestExpect(t, strings.Contains(fmt.Sprintf("%v", bot.LastSentWhat), "tpl1"), false, "should not contain tpl1") helpers.TestExpect(t, strings.Contains(fmt.Sprintf("%v", bot.LastSentWhat), "tpl2"), true, "but should contain tpl2") @@ -110,11 +110,11 @@ func TestTemplateAdd(t *testing.T) { bot := &MockBot{} bc.AddBotAndStart(bot) - bc.commandTemplates(&tb.Message{Chat: chat, Text: "/t add my template"}) + bc.commandTemplates(&MockContext{M: &tb.Message{Chat: chat, Text: "/t add my template"}}) helpers.TestStringContains(t, fmt.Sprintf("%v", bot.LastSentWhat), "parameter count mismatch", "parameter count mismatch response") // Step 1: Start template creation - bc.commandTemplates(&tb.Message{Chat: chat, Text: "/t add myTemplate"}) + bc.commandTemplates(&MockContext{M: &tb.Message{Chat: chat, Text: "/t add myTemplate"}}) helpers.TestStringContains(t, fmt.Sprintf("%v", bot.LastSentWhat), "Please provide a full transaction template", "template creation process response") helpers.TestExpect(t, bc.State.states[chatId(chat.ID)], ST_TPL, "state should show template process") helpers.TestExpect(t, bc.State.tplStates[chatId(chat.ID)], TemplateName("myTemplate"), "state should save template name") @@ -124,7 +124,7 @@ func TestTemplateAdd(t *testing.T) { WillReturnResult(sqlmock.NewResult(1, 1)) // Step 2: Send template - bc.handleTextState(&tb.Message{Chat: chat, Text: "template data"}) + bc.handleTextState(&MockContext{M: &tb.Message{Chat: chat, Text: "template data"}}) helpers.TestExpect(t, bc.State.states[chatId(chat.ID)], ST_NONE, "state should be clean again") @@ -149,7 +149,7 @@ func TestTemplateRm(t *testing.T) { WithArgs(12345, "myTemplate"). WillReturnResult(sqlmock.NewResult(1, 1)) - bc.commandTemplates(&tb.Message{Chat: chat, Text: "/template rm myTemplate"}) // also test for /template instead of short alias /t + bc.commandTemplates(&MockContext{M: &tb.Message{Chat: chat, Text: "/template rm myTemplate"}}) // also test for /template instead of short alias /t helpers.TestStringContains(t, fmt.Sprintf("%v", bot.LastSentWhat), "Successfully removed your template 'myTemplate'", "removed template") if err := mock.ExpectationsWereMet(); err != nil { @@ -180,7 +180,7 @@ func TestTemplateUse(t *testing.T) { ExpectQuery(`SELECT "value" FROM "bot::userSetting"`). WithArgs(chat.ID, helpers.USERSET_CUR). WillReturnRows(sqlmock.NewRows([]string{"value"}).AddRow("TEST_CURRENCY")) - bc.commandTemplates(&tb.Message{Chat: chat, Text: "/t test 2022-04-11"}) + bc.commandTemplates(&MockContext{M: &tb.Message{Chat: chat, Text: "/t test 2022-04-11"}}) helpers.TestStringContains(t, fmt.Sprintf("%v", bot.AllLastSentWhat[len(bot.AllLastSentWhat)-2]), "Creating a new transaction from your template 'test'", "template tx starting msg") helpers.TestStringContains(t, fmt.Sprintf("%v", bot.LastSentWhat), "amount", "asking for amount") @@ -206,8 +206,8 @@ func TestTemplateUse(t *testing.T) { `). WillReturnResult(sqlmock.NewResult(1, 1)) tx := bc.State.txStates[chatId(chat.ID)] - tx.Input(&tb.Message{Text: "10.51 EUR_TEST"}) // amount - bc.handleTextState(&tb.Message{Chat: chat, Text: "Buy something"}) // description (via handleTextState) + tx.Input(&tb.Message{Text: "10.51 EUR_TEST"}) // amount + bc.handleTextState(&MockContext{M: &tb.Message{Chat: chat, Text: "Buy something"}}) // description (via handleTextState) if err := mock.ExpectationsWereMet(); err != nil { t.Errorf("there were unfulfilled expectations: %s", err) diff --git a/bot/transactionBuilder.go b/bot/transactionBuilder.go index 334cbe2..b06f3ef 100644 --- a/bot/transactionBuilder.go +++ b/bot/transactionBuilder.go @@ -13,7 +13,7 @@ import ( "github.com/LucaBernstein/beancount-bot-tg/db/crud" c "github.com/LucaBernstein/beancount-bot-tg/helpers" "github.com/fatih/structs" - tb "gopkg.in/tucnak/telebot.v2" + tb "gopkg.in/telebot.v3" ) type Hint struct { diff --git a/bot/transactionBuilder_test.go b/bot/transactionBuilder_test.go index aa3221d..38d90d4 100644 --- a/bot/transactionBuilder_test.go +++ b/bot/transactionBuilder_test.go @@ -9,7 +9,7 @@ import ( "github.com/LucaBernstein/beancount-bot-tg/bot" "github.com/LucaBernstein/beancount-bot-tg/helpers" - tb "gopkg.in/tucnak/telebot.v2" + tb "gopkg.in/telebot.v3" ) func TestTypesTemplateHintsOnlyContainsAllowed(t *testing.T) { diff --git a/bot/wrapper.go b/bot/wrapper.go index ed2d5c7..608a89c 100644 --- a/bot/wrapper.go +++ b/bot/wrapper.go @@ -1,13 +1,13 @@ package bot import ( - tb "gopkg.in/tucnak/telebot.v2" + tb "gopkg.in/telebot.v3" ) type IBot interface { // Using from base package: Start() - Handle(endpoint interface{}, handler interface{}) + Handle(endpoint interface{}, h tb.HandlerFunc, m ...tb.MiddlewareFunc) Send(to tb.Recipient, what interface{}, options ...interface{}) (*tb.Message, error) Respond(c *tb.Callback, resp ...*tb.CallbackResponse) error // custom by me: @@ -22,7 +22,7 @@ func (b *Bot) Start() { b.bot.Start() } -func (b *Bot) Handle(endpoint interface{}, handler interface{}) { +func (b *Bot) Handle(endpoint interface{}, handler tb.HandlerFunc, m ...tb.MiddlewareFunc) { b.bot.Handle(endpoint, handler) } diff --git a/db/crud/auth_user.go b/db/crud/auth_user.go index 5a4db69..8d84c8e 100644 --- a/db/crud/auth_user.go +++ b/db/crud/auth_user.go @@ -7,7 +7,7 @@ import ( "time" "github.com/LucaBernstein/beancount-bot-tg/helpers" - tb "gopkg.in/tucnak/telebot.v2" + tb "gopkg.in/telebot.v3" ) func IsGroupChat(m *tb.Message) bool { @@ -167,7 +167,8 @@ func (r *Repo) UserGetNotificationSetting(m *tb.Message) (daysDelay, hour int, e return -1, -1, nil } -/** +/* +* UserSetNotificationSetting sets user's notification settings. If daysDelay is < 0, schedule will be disabled. */ diff --git a/db/crud/auth_user_test.go b/db/crud/auth_user_test.go index fc406b8..9e89326 100644 --- a/db/crud/auth_user_test.go +++ b/db/crud/auth_user_test.go @@ -8,7 +8,7 @@ import ( "github.com/DATA-DOG/go-sqlmock" "github.com/LucaBernstein/beancount-bot-tg/db/crud" "github.com/LucaBernstein/beancount-bot-tg/helpers" - tb "gopkg.in/tucnak/telebot.v2" + tb "gopkg.in/telebot.v3" ) func TestEnrichUserData(t *testing.T) { diff --git a/db/crud/bot_cache.go b/db/crud/bot_cache.go index d50fecb..9704d71 100644 --- a/db/crud/bot_cache.go +++ b/db/crud/bot_cache.go @@ -4,7 +4,7 @@ import ( "database/sql" "github.com/LucaBernstein/beancount-bot-tg/helpers" - tb "gopkg.in/tucnak/telebot.v2" + tb "gopkg.in/telebot.v3" ) var CACHE_LOCAL = make(map[int64]map[string][]string) diff --git a/db/crud/bot_cache_test.go b/db/crud/bot_cache_test.go index ff031dd..b4c3fac 100644 --- a/db/crud/bot_cache_test.go +++ b/db/crud/bot_cache_test.go @@ -7,7 +7,7 @@ import ( "github.com/DATA-DOG/go-sqlmock" "github.com/LucaBernstein/beancount-bot-tg/bot" "github.com/LucaBernstein/beancount-bot-tg/db/crud" - tb "gopkg.in/tucnak/telebot.v2" + tb "gopkg.in/telebot.v3" ) func TestCacheOnlySuggestible(t *testing.T) { diff --git a/db/crud/bot_templates.go b/db/crud/bot_templates.go index b3be0bd..0e7961e 100644 --- a/db/crud/bot_templates.go +++ b/db/crud/bot_templates.go @@ -4,7 +4,7 @@ import ( "fmt" "github.com/LucaBernstein/beancount-bot-tg/helpers" - tb "gopkg.in/tucnak/telebot.v2" + tb "gopkg.in/telebot.v3" ) type TemplateResult struct { diff --git a/db/crud/bot_templates_test.go b/db/crud/bot_templates_test.go index 6eea428..8bc5335 100644 --- a/db/crud/bot_templates_test.go +++ b/db/crud/bot_templates_test.go @@ -6,7 +6,7 @@ import ( "github.com/DATA-DOG/go-sqlmock" "github.com/LucaBernstein/beancount-bot-tg/helpers" - "gopkg.in/tucnak/telebot.v2" + "gopkg.in/telebot.v3" ) func TestGetTemplates(t *testing.T) { diff --git a/db/crud/bot_transaction.go b/db/crud/bot_transaction.go index 1c86851..9185096 100644 --- a/db/crud/bot_transaction.go +++ b/db/crud/bot_transaction.go @@ -4,7 +4,7 @@ import ( "fmt" "github.com/LucaBernstein/beancount-bot-tg/helpers" - tb "gopkg.in/tucnak/telebot.v2" + tb "gopkg.in/telebot.v3" ) func (r *Repo) RecordTransaction(chatId int64, tx string) error { diff --git a/db/crud/bot_transaction_test.go b/db/crud/bot_transaction_test.go index 3caa489..65506a5 100644 --- a/db/crud/bot_transaction_test.go +++ b/db/crud/bot_transaction_test.go @@ -6,7 +6,7 @@ import ( "github.com/DATA-DOG/go-sqlmock" "github.com/LucaBernstein/beancount-bot-tg/db/crud" - tb "gopkg.in/tucnak/telebot.v2" + tb "gopkg.in/telebot.v3" ) func TestRecordGetTransaction(t *testing.T) { diff --git a/db/crud/bot_userSettings.go b/db/crud/bot_userSettings.go index 4c3feef..a3f37eb 100644 --- a/db/crud/bot_userSettings.go +++ b/db/crud/bot_userSettings.go @@ -6,7 +6,7 @@ import ( "strconv" "github.com/LucaBernstein/beancount-bot-tg/helpers" - tb "gopkg.in/tucnak/telebot.v2" + tb "gopkg.in/telebot.v3" ) func (r *Repo) GetUserSetting(setting string, tgChatId int64) (exists bool, val string, err error) { diff --git a/db/crud/bot_userSettings_test.go b/db/crud/bot_userSettings_test.go index 79976b9..e9cd2a8 100644 --- a/db/crud/bot_userSettings_test.go +++ b/db/crud/bot_userSettings_test.go @@ -7,7 +7,7 @@ import ( "github.com/DATA-DOG/go-sqlmock" "github.com/LucaBernstein/beancount-bot-tg/db/crud" "github.com/LucaBernstein/beancount-bot-tg/helpers" - tb "gopkg.in/tucnak/telebot.v2" + tb "gopkg.in/telebot.v3" ) func TestTzOffsetSettingAndGetting(t *testing.T) { diff --git a/db/crud/logger.go b/db/crud/logger.go index 191f24b..8c31151 100644 --- a/db/crud/logger.go +++ b/db/crud/logger.go @@ -4,7 +4,7 @@ import ( "log" "github.com/LucaBernstein/beancount-bot-tg/helpers" - tb "gopkg.in/tucnak/telebot.v2" + tb "gopkg.in/telebot.v3" ) var TEST_MODE = false diff --git a/go.mod b/go.mod index 716f13e..e139f9d 100644 --- a/go.mod +++ b/go.mod @@ -3,12 +3,12 @@ module github.com/LucaBernstein/beancount-bot-tg go 1.17 require ( + github.com/fatih/structs v1.1.0 github.com/go-co-op/gocron v1.13.0 - gopkg.in/tucnak/telebot.v2 v2.5.0 + gopkg.in/telebot.v3 v3.0.0 ) require ( - github.com/fatih/structs v1.1.0 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect ) @@ -16,5 +16,4 @@ require ( require ( github.com/DATA-DOG/go-sqlmock v1.5.0 github.com/lib/pq v1.10.5 - github.com/pkg/errors v0.9.1 // indirect ) diff --git a/go.sum b/go.sum index 0c4ab13..70a7e0e 100644 --- a/go.sum +++ b/go.sum @@ -3,28 +3,54 @@ github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/go-co-op/gocron v1.13.0 h1:BjkuNImPy5NuIPEifhWItFG7pYyr27cyjS6BN9w/D4c= github.com/go-co-op/gocron v1.13.0/go.mod h1:GD5EIEly1YNW+LovFVx5dzbYVcIc8544K99D8UVRpGo= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= +github.com/goccy/go-yaml v1.9.5/go.mod h1:U/jl18uSupI5rdI2jmuCswEA2htH9eXfferR3KfscvA= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/lib/pq v1.10.5 h1:J+gdV2cUmX7ZqL2B0lFcW0m+egaHC2V3lpO8nWxyYiQ= github.com/lib/pq v1.10.5/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/tucnak/telebot.v2 v2.5.0 h1:i+NynLo443Vp+Zn3Gv9JBjh3Z/PaiKAQwcnhNI7y6Po= -gopkg.in/tucnak/telebot.v2 v2.5.0/go.mod h1:BgaIIx50PSRS9pG59JH+geT82cfvoJU/IaI5TJdN3v8= +gopkg.in/telebot.v3 v3.0.0 h1:UgHIiE/RdjoDi6nf4xACM7PU3TqiPVV9vvTydCEnrTo= +gopkg.in/telebot.v3 v3.0.0/go.mod h1:7rExV8/0mDDNu9epSrDm/8j22KLaActH1Tbee6YjzWg= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= diff --git a/helpers/logger.go b/helpers/logger.go index d16761d..33b2d43 100644 --- a/helpers/logger.go +++ b/helpers/logger.go @@ -5,7 +5,7 @@ import ( "log" "strconv" - tb "gopkg.in/tucnak/telebot.v2" + tb "gopkg.in/telebot.v3" ) type Level int diff --git a/helpers/logger_test.go b/helpers/logger_test.go index 30bc15c..13391e7 100644 --- a/helpers/logger_test.go +++ b/helpers/logger_test.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/LucaBernstein/beancount-bot-tg/helpers" - tb "gopkg.in/tucnak/telebot.v2" + tb "gopkg.in/telebot.v3" ) func TestLogLocal(t *testing.T) { diff --git a/helpers/subcommands.go b/helpers/subcommands.go index 3ed2aed..2abd948 100644 --- a/helpers/subcommands.go +++ b/helpers/subcommands.go @@ -4,7 +4,7 @@ import ( "fmt" "strings" - tb "gopkg.in/tucnak/telebot.v2" + tb "gopkg.in/telebot.v3" ) type handlerFunc func(m *tb.Message, params ...string) diff --git a/helpers/subcommands_test.go b/helpers/subcommands_test.go index 1c46201..100b6e7 100644 --- a/helpers/subcommands_test.go +++ b/helpers/subcommands_test.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/LucaBernstein/beancount-bot-tg/helpers" - tb "gopkg.in/tucnak/telebot.v2" + tb "gopkg.in/telebot.v3" ) type subcommandTestState struct {