Skip to content

Commit

Permalink
feat: added user first/last/nick name validation (#42)
Browse files Browse the repository at this point in the history
* feat: added user first/last/nick name validation

* fix: added top level t.Parallel() call

---------

Co-authored-by: hrvadl <[email protected]>
  • Loading branch information
hrvadl and hrvadl authored Jun 2, 2024
1 parent 0d55435 commit 9f79226
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 2 deletions.
39 changes: 37 additions & 2 deletions internal/handler/join/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@ package join

import (
"context"
"fmt"
"log/slog"

"github.com/mymmrac/telego"
th "github.com/mymmrac/telego/telegohandler"
tu "github.com/mymmrac/telego/telegoutil"
"github.com/spf13/viper"

"github.com/GolangUA/telegram-butler/internal/handler/callback/callbackdata"
"github.com/GolangUA/telegram-butler/internal/handler/join/validator"
"github.com/GolangUA/telegram-butler/internal/messages"
"github.com/GolangUA/telegram-butler/internal/module/logger"
)
Expand All @@ -19,11 +22,19 @@ const (
)

func Register(bh *th.BotHandler) {
h := &handler{}
h := &handler{
validator: validator.New(validator.DefaultForbiddenList),
}
bh.HandleChatJoinRequestCtx(h.chatJoinRequest)
}

type handler struct{}
type nameValidator interface {
Validate(names ...string) bool
}

type handler struct {
validator nameValidator
}

func (h *handler) chatJoinRequest(ctx context.Context, bot *telego.Bot, request telego.ChatJoinRequest) {
log := logger.FromContext(ctx)
Expand All @@ -35,6 +46,30 @@ func (h *handler) chatJoinRequest(ctx context.Context, bot *telego.Bot, request

log.Info("[JOIN REQUEST]")

if !h.validator.Validate(request.From.FirstName, request.From.LastName, request.From.Username) {
log.Info("Name validation is failed")
err := bot.DeclineChatJoinRequest(&telego.DeclineChatJoinRequestParams{
UserID: request.From.ID,
ChatID: tu.ID(request.Chat.ID),
})
if err != nil {
log.Error("Decline join request failed", slog.Any("error", err))
return
}

msg := fmt.Sprintf(messages.Decline, viper.GetString("admin-username"))
_, err = bot.SendMessage(&telego.SendMessageParams{
ChatID: tu.ID(request.From.ID),
Text: msg,
ReplyMarkup: tu.ReplyKeyboardRemove(),
})
if err != nil {
log.Error("Sending decision message failed", slog.Any("error", err))
}

return
}

k := tu.InlineKeyboard(
tu.InlineKeyboardRow(
telego.InlineKeyboardButton{
Expand Down
7 changes: 7 additions & 0 deletions internal/handler/join/validator/lists.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package validator

var DefaultForbiddenList = []string{
"🇷🇺",
"🪆",
"Russia",
}
29 changes: 29 additions & 0 deletions internal/handler/join/validator/name.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package validator

import "strings"

func New(forbiddenWords []string) *ByName {
for i, w := range forbiddenWords {
forbiddenWords[i] = strings.ToLower(w)
}

return &ByName{
forbiddenWords: forbiddenWords,
}
}

type ByName struct {
forbiddenWords []string
}

func (v *ByName) Validate(names ...string) bool {
for _, name := range names {
lowerName := strings.ToLower(name)
for _, word := range v.forbiddenWords {
if strings.Contains(lowerName, word) {
return false
}
}
}
return true
}
84 changes: 84 additions & 0 deletions internal/handler/join/validator/name_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package validator

import "testing"

func TestValidateByName(t *testing.T) {
t.Parallel()
tc := []struct {
name string
forbiddenWords []string
words []string
expected bool
}{
{
name: "Should forbid russian flag emoji",
forbiddenWords: []string{"🇷🇺"},
words: []string{"🇷🇺"},
expected: false,
},
{
name: "Should forbid russian matryoshka emoji",
forbiddenWords: []string{"🪆"},
words: []string{"🪆"},
expected: false,
},
{
name: "Should forbid word 'Russia'",
forbiddenWords: []string{"Russia"},
words: []string{"Russia"},
expected: false,
},
{
name: "Should forbid word 'russia'",
forbiddenWords: []string{"russia"},
words: []string{"russia"},
expected: false,
},
{
name: "Should allow 'Vadym' name to bypass",
forbiddenWords: []string{"russia"},
words: []string{"Vadym"},
expected: true,
},
{
name: "Should allow 'Ruslan' name to bypass",
forbiddenWords: []string{"russia"},
words: []string{"Ruslan"},
expected: true,
},
{
name: "Should allow anything when forbidden words are empty",
forbiddenWords: []string{},
words: []string{"russia", "Russia"},
expected: true,
},
{
name: "Should allow anything when nil slice of forbidden words are passed",
forbiddenWords: nil,
words: []string{"russia", "Russia"},
expected: true,
},
{
name: "Should allow when empty slice of words are passed",
forbiddenWords: []string{"russia"},
words: []string{},
expected: true,
},
{
name: "Should allow when nil slice of words are passed",
forbiddenWords: []string{"russia"},
words: nil,
expected: true,
},
}

for _, tt := range tc {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
v := New(tt.forbiddenWords)
if got := v.Validate(tt.words...); got != tt.expected {
t.Errorf("Expected to get: %v, but got: %v", tt.expected, got)
}
})
}
}

0 comments on commit 9f79226

Please sign in to comment.