Skip to content

Commit

Permalink
Implement custom field names (#186)
Browse files Browse the repository at this point in the history
  • Loading branch information
LucaBernstein authored Sep 12, 2022
1 parent 2c504c0 commit 6e45cf3
Show file tree
Hide file tree
Showing 29 changed files with 614 additions and 380 deletions.
5 changes: 5 additions & 0 deletions ACKNOWLEDGEMENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ Errors.
https://github.com/pkg/errors.
Copyright (c) 2015, Dave Cheney <[email protected]>.
BSD-2-Clause "Simplified" License (https://github.com/pkg/errors/blob/master/LICENSE).
Structs.
https://github.com/fatih/structs.
Copyright (c) 2014 Fatih Arslan.
MIT license (https://github.com/fatih/structs/blob/master/LICENSE).
```

---
Expand Down
9 changes: 1 addition & 8 deletions bot/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -411,11 +411,7 @@ func (bc *BotController) configHandleAccountDelete(m *tb.Message, params ...stri

func (bc *BotController) deleteUserData(m *tb.Message) {
errors := errors{operation: "user deletion", bc: bc, m: m}
errors.handle2(bc.Repo.DeleteCacheEntries(m, helpers.STX_ACCT, ""))
errors.handle2(bc.Repo.DeleteCacheEntries(m, helpers.STX_ACCF, ""))
errors.handle2(bc.Repo.DeleteCacheEntries(m, helpers.STX_AMTF, ""))
errors.handle2(bc.Repo.DeleteCacheEntries(m, helpers.STX_DATE, ""))
errors.handle2(bc.Repo.DeleteCacheEntries(m, helpers.STX_DESC, ""))
errors.handle1(bc.Repo.DeleteAllCacheEntries(m))

errors.handle1(bc.Repo.UserSetNotificationSetting(m, -1, -1))

Expand Down Expand Up @@ -443,6 +439,3 @@ func (e *errors) handle1(err error) {
e.bc.Logf(ERROR, e.m, "Handling error for operation '%s' (failing silently, proceeding): %s", e.operation, err.Error())
}
}
func (e *errors) handle2(_ interface{}, err error) {
e.handle1(err)
}
31 changes: 3 additions & 28 deletions bot/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,31 +55,6 @@ func (bc *BotController) AddBotAndStart(b IBot) {

b.Handle(tb.OnText, bc.handleTextState)

// Todo: Add generic callback handler
// Route callback by ID splits
bc.Bot.Handle(&btnSuggListAccFrom, func(c *tb.Callback) {
bc.Logf(DEBUG, nil, "Handling callback on button. Chat: %d", c.Message.Chat.ID)
c.Message.Text = "/suggestions list accFrom"
// TODO: What happens in group chats?
c.Message.Sender = &tb.User{ID: c.Message.Chat.ID} // hack to send chat user a message (in private chats userId = chatId)
bc.suggestionsHandler(c.Message)
bc.Bot.Respond(c, &tb.CallbackResponse{}) // Always respond
})
bc.Bot.Handle(&btnSuggListAccTo, func(c *tb.Callback) {
bc.Logf(DEBUG, nil, "Handling callback on button. Chat: %d", c.Message.Chat.ID)
c.Message.Text = "/suggestions list accTo"
c.Message.Sender = &tb.User{ID: c.Message.Chat.ID}
bc.suggestionsHandler(c.Message)
bc.Bot.Respond(c, &tb.CallbackResponse{}) // Always respond
})
bc.Bot.Handle(&btnSuggListTxDesc, func(c *tb.Callback) {
bc.Logf(DEBUG, nil, "Handling callback on button. Chat: %d", c.Message.Chat.ID)
c.Message.Text = "/suggestions list txDesc"
c.Message.Sender = &tb.User{ID: c.Message.Chat.ID}
bc.suggestionsHandler(c.Message)
bc.Bot.Respond(c, &tb.CallbackResponse{}) // Always respond
})

bc.Logf(TRACE, nil, "Starting bot '%s'", b.Me().Username)

if bc.CronScheduler != nil {
Expand Down Expand Up @@ -640,7 +615,7 @@ func (bc *BotController) handleTextState(m *tb.Message) {
return
} else if state == ST_TX {
tx := bc.State.GetTx(m)
err := tx.Input(m)
_, err := tx.Input(m)
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"+
Expand Down Expand Up @@ -672,7 +647,7 @@ func (bc *BotController) handleTextState(m *tb.Message) {
func (bc *BotController) sendNextTxHint(hint *Hint, m *tb.Message) {
replyKeyboard := ReplyKeyboard(hint.KeyboardOptions)
bc.Logf(TRACE, m, "Sending hints for next step: %v", hint.KeyboardOptions)
_, err := bc.Bot.Send(Recipient(m), escapeCharacters(hint.Prompt, "(", ")", "."), replyKeyboard, tb.ModeMarkdownV2)
_, err := bc.Bot.Send(Recipient(m), escapeCharacters(hint.Prompt, "(", ")", ".", "!"), replyKeyboard, tb.ModeMarkdownV2)
if err != nil {
bc.Logf(ERROR, m, "Sending bot message failed: %s", err.Error())
}
Expand Down Expand Up @@ -707,7 +682,7 @@ func (bc *BotController) finishTransaction(m *tb.Message, tx Tx) {
}

// TODO: Goroutine
err = bc.Repo.PutCacheHints(m, tx.DataKeys())
err = bc.Repo.PutCacheHints(m, tx.CacheData())
if err != nil {
bc.Logf(ERROR, m, "Something went wrong while caching transaction. Error: %s", err.Error())
// Don't return, instead continue flow (if recording was successful)
Expand Down
18 changes: 9 additions & 9 deletions bot/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,10 @@ func TestTextHandlingWithoutPriorState(t *testing.T) {
// Create simple tx and fill it completely
bc.commandCreateSimpleTx(&tb.Message{Chat: chat})
tx := bc.State.txStates[12345]
tx.Input(&tb.Message{Text: "17.34"}) // amount
tx.Input(&tb.Message{Text: "Assets:Wallet"}) // from
tx.Input(&tb.Message{Text: "Expenses:Groceries"}) // to
bc.handleTextState(&tb.Message{Chat: chat, Text: "Buy something in the grocery store"}) // description (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(&tb.Message{Chat: chat, Text: "Expenses:Groceries"}) // to (via handleTextState)

// After the first tx is done, send some command
m := &tb.Message{Chat: chat}
Expand Down Expand Up @@ -118,7 +118,7 @@ func TestStartTransactionWithPlainAmountThousandsSeparated(t *testing.T) {
bc.handleTextState(&tb.Message{Chat: chat, Text: "1,000,000"})

debugString := bc.State.txStates[12345].Debug()
expected := "data=[1000000.00"
expected := "data=map[amount::${SPACE_FORMAT}1000000.00"
helpers.TestStringContains(t, debugString, expected, "contain parsed amount")

if err := mock.ExpectationsWereMet(); err != nil {
Expand Down Expand Up @@ -394,10 +394,10 @@ func TestTimezoneOffsetForAutomaticDate(t *testing.T) {
// Create simple tx and fill it completely
bc.commandCreateSimpleTx(&tb.Message{Chat: chat})
tx := bc.State.txStates[12345]
tx.Input(&tb.Message{Text: "17.34"}) // amount
tx.Input(&tb.Message{Text: "Assets:Wallet"}) // from
tx.Input(&tb.Message{Text: "Expenses:Groceries"}) // to
bc.handleTextState(&tb.Message{Chat: chat, Text: "Buy something in the grocery store"}) // description (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(&tb.Message{Chat: chat, Text: "Expenses:Groceries"}) // to (via handleTextState)

// After the first tx is done, send some command
m := &tb.Message{Chat: chat}
Expand Down
3 changes: 3 additions & 0 deletions bot/replyKeyboards.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import (
)

func ReplyKeyboard(buttons []string) *tb.ReplyMarkup {
if len(buttons) == 0 {
return clearKeyboard()
}
kb := &tb.ReplyMarkup{ResizeReplyKeyboard: true, OneTimeKeyboard: true}
buttonsCreated := []tb.Row{}
for _, label := range buttons {
Expand Down
36 changes: 19 additions & 17 deletions bot/suggestions.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,11 @@ import (
tb "gopkg.in/tucnak/telebot.v2"
)

var (
suggestionsMenu = &tb.ReplyMarkup{}
btnSuggListAccFrom = suggestionsMenu.Data("/suggestions list accFrom", "btnSuggestionsListAccFrom")
btnSuggListAccTo = suggestionsMenu.Data("/suggestions list accTo", "btnSuggestionsListAccTo")
btnSuggListTxDesc = suggestionsMenu.Data("/suggestions list txDesc", "btnSuggestionsListTxDesc")
)
func isAllowedSuggestionType(s string) bool {
splits := strings.SplitN(s, ":", 2)
_, exists := TEMPLATE_TYPE_HINTS[Type(splits[0])]
return exists
}

func (bc *BotController) suggestionsHandler(m *tb.Message) {
sc := h.MakeSubcommandHandler("/"+CMD_SUGGEST, true)
Expand All @@ -28,24 +27,24 @@ func (bc *BotController) suggestionsHandler(m *tb.Message) {
}

func (bc *BotController) suggestionsHelp(m *tb.Message, err error) {
suggestionTypes := strings.Join(h.AllowedSuggestionTypes(), ", ")
suggestionTypes := []string{}
for _, suggType := range h.AllowedSuggestionTypes() {
if suggType == h.FIELD_ACCOUNT {
suggType += ":[from,to,...]"
}
suggestionTypes = append(suggestionTypes, suggType)
}
errorMsg := ""
if err != nil {
errorMsg += fmt.Sprintf("Error executing your command: %s\n\n", err.Error())
}

suggestionsMenu.Inline(
suggestionsMenu.Row(btnSuggListAccFrom),
suggestionsMenu.Row(btnSuggListAccTo),
suggestionsMenu.Row(btnSuggListTxDesc),
)

_, err = bc.Bot.Send(Recipient(m), errorMsg+fmt.Sprintf(`Usage help for /suggestions:
/suggestions list <type>
/suggestions add <type> <value>
/suggestions rm <type> [value]
Parameter <type> is one from: [%s]`, suggestionTypes), suggestionsMenu)
Parameter <type> is one from: [%s]`, strings.Join(suggestionTypes, ", ")))
if err != nil {
bc.Logf(ERROR, m, "Sending bot message failed: %s", err.Error())
}
Expand All @@ -57,7 +56,8 @@ func (bc *BotController) suggestionsHandleList(m *tb.Message, params ...string)
bc.suggestionsHelp(m, fmt.Errorf("error encountered while retrieving suggestions list: %s", err.Error()))
return
}
if !h.ArrayContainsC(h.AllowedSuggestionTypes(), p.T, false) {
p.T = h.FqCacheKey(p.T)
if !isAllowedSuggestionType(p.T) {
bc.suggestionsHelp(m, fmt.Errorf("unexpected subcommand"))
return
}
Expand Down Expand Up @@ -93,7 +93,8 @@ func (bc *BotController) suggestionsHandleAdd(m *tb.Message, params ...string) {
bc.suggestionsHelp(m, fmt.Errorf("error encountered while retrieving suggestions list: %s", err.Error()))
return
}
if !h.ArrayContainsC(h.AllowedSuggestionTypes(), p.T, false) {
p.T = h.FqCacheKey(p.T)
if !isAllowedSuggestionType(p.T) {
bc.suggestionsHelp(m, fmt.Errorf("unexpected subcommand"))
return
}
Expand Down Expand Up @@ -121,7 +122,8 @@ func (bc *BotController) suggestionsHandleRemove(m *tb.Message, params ...string
bc.suggestionsHelp(m, fmt.Errorf("error encountered while retrieving suggestions list: %s", err.Error()))
return
}
if !h.ArrayContainsC(h.AllowedSuggestionTypes(), p.T, false) {
p.T = h.FqCacheKey(p.T)
if !isAllowedSuggestionType(p.T) {
bc.suggestionsHelp(m, fmt.Errorf("unexpected subcommand"))
return
}
Expand Down
13 changes: 7 additions & 6 deletions bot/suggestions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ func TestSuggestionsHandlingWithSpaces(t *testing.T) {
}
mock.
ExpectExec(`DELETE FROM "bot::cache"`).
WithArgs(12345, "txDesc", "Some description with spaces").
WithArgs(12345, "description:", "Some description with spaces").
WillReturnResult(sqlmock.NewResult(1, 1))
mock.
ExpectExec(`DELETE FROM "bot::cache"`).
WithArgs(12345, "txDesc", "SomeDescriptionWithoutSpaces").
WithArgs(12345, "description:", "SomeDescriptionWithoutSpaces").
WillReturnResult(sqlmock.NewResult(1, 1))

bc := NewBotController(db)
Expand All @@ -36,30 +36,31 @@ func TestSuggestionsHandlingWithSpaces(t *testing.T) {
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})
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 txDesc Too Many arguments with spaces", Chat: chat})
bc.commandSuggestions(&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 txDesc \"Some description with spaces\"", Chat: chat})
bc.commandSuggestions(&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 txDesc \"SomeDescriptionWithoutSpaces\"", Chat: chat})
bc.commandSuggestions(&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 txDesc ", Chat: chat})
bc.commandSuggestions(&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)
}
Expand Down
13 changes: 4 additions & 9 deletions bot/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,9 @@ func (bc *BotController) templatesHandleAdd(m *tb.Message, params ...string) {
- ${amount}, ${-amount}, ${amount/i} (e.g. ${amount/2})
- ${date}
- ${description}
- ${from}
- ${to}
- ${account:from}
- ${account:to}
- ${account:<yourName>:<yourHint>}
Example:
Expand Down Expand Up @@ -212,13 +213,7 @@ func (bc *BotController) templatesUse(m *tb.Message, params ...string) error {
bc.finishTransaction(m, tx)
return nil
}
// TODO: Refactor
hint := tx.NextHint(bc.Repo, m)
replyKeyboard := ReplyKeyboard(hint.KeyboardOptions)
bc.Logf(TRACE, m, "Sending hints for next step: %v", hint.KeyboardOptions)
_, err = bc.Bot.Send(Recipient(m), hint.Prompt, replyKeyboard)
if err != nil {
bc.Logf(ERROR, m, "Sending bot message failed: %s", err.Error())
}
bc.sendNextTxHint(hint, m)
return nil
}
11 changes: 4 additions & 7 deletions bot/templates_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,11 +176,14 @@ func TestTemplateUse(t *testing.T) {
fromFix ${-amount}
toFix1 ${amount/2}
toFix2 ${amount/2}`))

mock.
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"})
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")

mock.
ExpectQuery(`SELECT "value" FROM "bot::userSetting"`).
WithArgs(chat.ID, helpers.USERSET_CUR).
Expand All @@ -193,7 +196,6 @@ func TestTemplateUse(t *testing.T) {
ExpectQuery(`SELECT "value" FROM "bot::userSetting"`).
WithArgs(chat.ID, helpers.USERSET_TZOFF).
WillReturnRows(sqlmock.NewRows([]string{"value"}))

mock.
ExpectExec(regexp.QuoteMeta(`INSERT INTO "bot::transaction" ("tgChatId", "value")
VALUES ($1, $2);`)).
Expand All @@ -203,11 +205,6 @@ func TestTemplateUse(t *testing.T) {
toFix2 5.255 EUR_TEST
`).
WillReturnResult(sqlmock.NewResult(1, 1))

bc.commandTemplates(&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")

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)
Expand Down
Loading

0 comments on commit 6e45cf3

Please sign in to comment.