From 9a493c6186f6b95af722afabb7a5e886e7413614 Mon Sep 17 00:00:00 2001 From: Umputun Date: Sun, 24 Dec 2023 15:36:00 -0600 Subject: [PATCH] use spam bot as detector for webapi it already loads and monitor all sample files --- .gitignore | 2 +- app/bot/mocks/detector.go | 48 +++++++++++++++++++++++++++ app/bot/spam.go | 21 ++++++------ app/bot/spam_test.go | 12 +++---- app/main.go | 49 ++++++++++++++++++---------- app/main_test.go | 9 +++-- app/webapi/mocks/detector.go | 18 +++++----- app/webapi/testdata/ham-samples.txt | 1 + app/webapi/testdata/spam-samples.txt | 1 + app/webapi/webapi.go | 30 ++++++++--------- app/webapi/webapi_test.go | 14 ++++---- lib/lib_test.go | 2 +- 12 files changed, 138 insertions(+), 69 deletions(-) create mode 100644 app/webapi/testdata/ham-samples.txt create mode 100644 app/webapi/testdata/spam-samples.txt diff --git a/.gitignore b/.gitignore index 837308e..8404c56 100644 --- a/.gitignore +++ b/.gitignore @@ -26,5 +26,5 @@ go.work data/spam-dynamic.txt data/ham-dynamic.txt data/approved-users.txt -data/tg-spam.db +tg-spam.db logs/ \ No newline at end of file diff --git a/app/bot/mocks/detector.go b/app/bot/mocks/detector.go index f7b0d2f..a0b8c55 100644 --- a/app/bot/mocks/detector.go +++ b/app/bot/mocks/detector.go @@ -18,6 +18,9 @@ import ( // AddApprovedUsersFunc: func(ids ...string) { // panic("mock out the AddApprovedUsers method") // }, +// ApprovedUsersFunc: func() []string { +// panic("mock out the ApprovedUsers method") +// }, // CheckFunc: func(msg string, userID string) (bool, []lib.CheckResult) { // panic("mock out the Check method") // }, @@ -46,6 +49,9 @@ type DetectorMock struct { // AddApprovedUsersFunc mocks the AddApprovedUsers method. AddApprovedUsersFunc func(ids ...string) + // ApprovedUsersFunc mocks the ApprovedUsers method. + ApprovedUsersFunc func() []string + // CheckFunc mocks the Check method. CheckFunc func(msg string, userID string) (bool, []lib.CheckResult) @@ -71,6 +77,9 @@ type DetectorMock struct { // Ids is the ids argument value. Ids []string } + // ApprovedUsers holds details about calls to the ApprovedUsers method. + ApprovedUsers []struct { + } // Check holds details about calls to the Check method. Check []struct { // Msg is the msg argument value. @@ -109,6 +118,7 @@ type DetectorMock struct { } } lockAddApprovedUsers sync.RWMutex + lockApprovedUsers sync.RWMutex lockCheck sync.RWMutex lockLoadSamples sync.RWMutex lockLoadStopWords sync.RWMutex @@ -156,6 +166,40 @@ func (mock *DetectorMock) ResetAddApprovedUsersCalls() { mock.lockAddApprovedUsers.Unlock() } +// ApprovedUsers calls ApprovedUsersFunc. +func (mock *DetectorMock) ApprovedUsers() []string { + if mock.ApprovedUsersFunc == nil { + panic("DetectorMock.ApprovedUsersFunc: method is nil but Detector.ApprovedUsers was just called") + } + callInfo := struct { + }{} + mock.lockApprovedUsers.Lock() + mock.calls.ApprovedUsers = append(mock.calls.ApprovedUsers, callInfo) + mock.lockApprovedUsers.Unlock() + return mock.ApprovedUsersFunc() +} + +// ApprovedUsersCalls gets all the calls that were made to ApprovedUsers. +// Check the length with: +// +// len(mockedDetector.ApprovedUsersCalls()) +func (mock *DetectorMock) ApprovedUsersCalls() []struct { +} { + var calls []struct { + } + mock.lockApprovedUsers.RLock() + calls = mock.calls.ApprovedUsers + mock.lockApprovedUsers.RUnlock() + return calls +} + +// ResetApprovedUsersCalls reset all the calls that were made to ApprovedUsers. +func (mock *DetectorMock) ResetApprovedUsersCalls() { + mock.lockApprovedUsers.Lock() + mock.calls.ApprovedUsers = nil + mock.lockApprovedUsers.Unlock() +} + // Check calls CheckFunc. func (mock *DetectorMock) Check(msg string, userID string) (bool, []lib.CheckResult) { if mock.CheckFunc == nil { @@ -408,6 +452,10 @@ func (mock *DetectorMock) ResetCalls() { mock.calls.AddApprovedUsers = nil mock.lockAddApprovedUsers.Unlock() + mock.lockApprovedUsers.Lock() + mock.calls.ApprovedUsers = nil + mock.lockApprovedUsers.Unlock() + mock.lockCheck.Lock() mock.calls.Check = nil mock.lockCheck.Unlock() diff --git a/app/bot/spam.go b/app/bot/spam.go index b7dcaef..5125d5d 100644 --- a/app/bot/spam.go +++ b/app/bot/spam.go @@ -22,8 +22,8 @@ import ( // SpamFilter bot checks if a user is a spammer using lib.Detector // Reloads spam samples, stop words and excluded tokens on file change. type SpamFilter struct { - director Detector - params SpamConfig + Detector + params SpamConfig } // SpamConfig is a full set of parameters for spam bot @@ -54,11 +54,12 @@ type Detector interface { UpdateHam(msg string) error AddApprovedUsers(ids ...string) RemoveApprovedUsers(ids ...string) + ApprovedUsers() (res []string) } // NewSpamFilter creates new spam filter func NewSpamFilter(ctx context.Context, detector Detector, params SpamConfig) *SpamFilter { - res := &SpamFilter{director: detector, params: params} + res := &SpamFilter{Detector: detector, params: params} go func() { if err := res.watch(ctx, params.WatchDelay); err != nil { log.Printf("[WARN] samples file watcher failed: %v", err) @@ -73,7 +74,7 @@ func (s *SpamFilter) OnMessage(msg Message) (response Response) { return Response{} } displayUsername := DisplayName(msg) - isSpam, checkResults := s.director.Check(msg.Text, strconv.FormatInt(msg.From.ID, 10)) + isSpam, checkResults := s.Check(msg.Text, strconv.FormatInt(msg.From.ID, 10)) crs := []string{} for _, cr := range checkResults { crs = append(crs, fmt.Sprintf("{name: %s, spam: %v, details: %s}", cr.Name, cr.Spam, cr.Details)) @@ -97,7 +98,7 @@ func (s *SpamFilter) OnMessage(msg Message) (response Response) { // UpdateSpam appends a message to the spam samples file and updates the classifier func (s *SpamFilter) UpdateSpam(msg string) error { log.Printf("[DEBUG] update spam samples with %q", msg) - if err := s.director.UpdateSpam(msg); err != nil { + if err := s.Detector.UpdateSpam(msg); err != nil { return fmt.Errorf("can't update spam samples: %w", err) } return nil @@ -106,7 +107,7 @@ func (s *SpamFilter) UpdateSpam(msg string) error { // UpdateHam appends a message to the ham samples file and updates the classifier func (s *SpamFilter) UpdateHam(msg string) error { log.Printf("[DEBUG] update ham samples with %q", msg) - if err := s.director.UpdateHam(msg); err != nil { + if err := s.Detector.UpdateHam(msg); err != nil { return fmt.Errorf("can't update ham samples: %w", err) } return nil @@ -120,7 +121,7 @@ func (s *SpamFilter) AddApprovedUsers(id int64, ids ...int64) { for i, id := range combinedIDs { sids[i] = strconv.FormatInt(id, 10) } - s.director.AddApprovedUsers(sids...) + s.Detector.AddApprovedUsers(sids...) } // RemoveApprovedUsers removes users from the list of approved users @@ -131,7 +132,7 @@ func (s *SpamFilter) RemoveApprovedUsers(id int64, ids ...int64) { for i, id := range combinedIDs { sids[i] = strconv.FormatInt(id, 10) } - s.director.RemoveApprovedUsers(sids...) + s.Detector.RemoveApprovedUsers(sids...) } // watch watches for changes in samples files and reloads them @@ -239,13 +240,13 @@ func (s *SpamFilter) ReloadSamples() (err error) { defer hamDynamicReader.Close() // reload samples and stop-words. note: we don't need reset as LoadSamples and LoadStopWords clear the state first - lr, err := s.director.LoadSamples(exclReader, []io.Reader{spamReader, spamDynamicReader}, + lr, err := s.LoadSamples(exclReader, []io.Reader{spamReader, spamDynamicReader}, []io.Reader{hamReader, hamDynamicReader}) if err != nil { return fmt.Errorf("failed to reload samples: %w", err) } - ls, err := s.director.LoadStopWords(stopWordsReader) + ls, err := s.LoadStopWords(stopWordsReader) if err != nil { return fmt.Errorf("failed to reload stop words: %w", err) } diff --git a/app/bot/spam_test.go b/app/bot/spam_test.go index b3ea9d0..f5ffdd0 100644 --- a/app/bot/spam_test.go +++ b/app/bot/spam_test.go @@ -342,7 +342,7 @@ func TestAddApprovedUsers(t *testing.T) { t.Run("add single approved user", func(t *testing.T) { mockDirector.ResetCalls() - sf := SpamFilter{director: mockDirector} + sf := SpamFilter{Detector: mockDirector} sf.AddApprovedUsers(1) require.Equal(t, 1, len(mockDirector.AddApprovedUsersCalls())) assert.Equal(t, []string{"1"}, mockDirector.AddApprovedUsersCalls()[0].Ids) @@ -350,7 +350,7 @@ func TestAddApprovedUsers(t *testing.T) { t.Run("add multiple approved users", func(t *testing.T) { mockDirector.ResetCalls() - sf := SpamFilter{director: mockDirector} + sf := SpamFilter{Detector: mockDirector} sf.AddApprovedUsers(1, 2, 3) require.Equal(t, 1, len(mockDirector.AddApprovedUsersCalls())) assert.Equal(t, []string{"1", "2", "3"}, mockDirector.AddApprovedUsersCalls()[0].Ids) @@ -358,7 +358,7 @@ func TestAddApprovedUsers(t *testing.T) { t.Run("add empty list of approved users", func(t *testing.T) { mockDirector.ResetCalls() - sf := SpamFilter{director: mockDirector} + sf := SpamFilter{Detector: mockDirector} sf.AddApprovedUsers(1, 2, 3) require.Equal(t, 1, len(mockDirector.AddApprovedUsersCalls())) assert.Equal(t, []string{"1", "2", "3"}, mockDirector.AddApprovedUsersCalls()[0].Ids) @@ -370,7 +370,7 @@ func TestRemoveApprovedUsers(t *testing.T) { t.Run("remove single approved user", func(t *testing.T) { mockDirector.ResetCalls() - sf := SpamFilter{director: mockDirector} + sf := SpamFilter{Detector: mockDirector} sf.RemoveApprovedUsers(1) require.Equal(t, 1, len(mockDirector.RemoveApprovedUsersCalls())) assert.Equal(t, []string{"1"}, mockDirector.RemoveApprovedUsersCalls()[0].Ids) @@ -378,7 +378,7 @@ func TestRemoveApprovedUsers(t *testing.T) { t.Run("remove multiple approved users", func(t *testing.T) { mockDirector.ResetCalls() - sf := SpamFilter{director: mockDirector} + sf := SpamFilter{Detector: mockDirector} sf.RemoveApprovedUsers(1, 2, 3) require.Equal(t, 1, len(mockDirector.RemoveApprovedUsersCalls())) assert.Equal(t, []string{"1", "2", "3"}, mockDirector.RemoveApprovedUsersCalls()[0].Ids) @@ -386,7 +386,7 @@ func TestRemoveApprovedUsers(t *testing.T) { t.Run("remove empty list of approved users", func(t *testing.T) { mockDirector.ResetCalls() - sf := SpamFilter{director: mockDirector} + sf := SpamFilter{Detector: mockDirector} sf.RemoveApprovedUsers(1, 2, 3) require.Equal(t, 1, len(mockDirector.RemoveApprovedUsersCalls())) assert.Equal(t, []string{"1", "2", "3"}, mockDirector.RemoveApprovedUsersCalls()[0].Ids) diff --git a/app/main.go b/app/main.go index dbf9f66..e3808dc 100644 --- a/app/main.go +++ b/app/main.go @@ -33,8 +33,8 @@ import ( type options struct { Telegram struct { - Token string `long:"token" env:"TOKEN" description:"telegram bot token" required:"true"` - Group string `long:"group" env:"GROUP" description:"group name/id" required:"true"` + Token string `long:"token" env:"TOKEN" description:"telegram bot token"` + Group string `long:"group" env:"GROUP" description:"group name/id"` Timeout time.Duration `long:"timeout" env:"TIMEOUT" default:"30s" description:"http client timeout for telegram" ` IdleDuration time.Duration `long:"idle" env:"IDLE" default:"30s" description:"idle duration"` PreserveUnbanned bool `long:"preserve-unbanned" env:"PRESERVE_UNBANNED" description:"preserve user after unban"` @@ -153,6 +153,10 @@ func execute(ctx context.Context, opts options) error { log.Print("[WARN] dry mode, no actual bans") } + if !opts.Server.Enabled && (opts.Telegram.Token == "" || opts.Telegram.Group == "") { + return errors.New("telegram token and group are required") + } + // make samples and dynamic data dirs if err := os.MkdirAll(opts.Files.SamplesDataPath, 0o700); err != nil { return fmt.Errorf("can't make samples dir, %w", err) @@ -161,13 +165,6 @@ func execute(ctx context.Context, opts options) error { return fmt.Errorf("can't make dynamic dir, %w", err) } - // make telegram bot - tbAPI, err := tbapi.NewBotAPI(opts.Telegram.Token) - if err != nil { - return fmt.Errorf("can't make telegram bot, %w", err) - } - tbAPI.Debug = opts.TGDbg - // make detector with all sample files loaded detector := makeDetector(opts) @@ -178,7 +175,7 @@ func execute(ctx context.Context, opts options) error { } log.Printf("[DEBUG] data db: %s", dataFile) - // load approved users and start auto-save + // load approved users approvedUsersStore, auErr := storage.NewApprovedUsers(dataDB) if auErr != nil { return fmt.Errorf("can't make approved users store, %w", auErr) @@ -194,21 +191,35 @@ func execute(ctx context.Context, opts options) error { } else { log.Printf("[DEBUG] approved users from: %s, loaded: %d", dataFile, count) } - go autoSaveApprovedUsers(ctx, detector, approvedUsersStore, time.Minute*5) + + // make spam bot + spamBot, err := makeSpamBot(ctx, opts, detector) + if err != nil { + return fmt.Errorf("can't make spam bot, %w", err) + } // activate web server if enabled if opts.Server.Enabled { // server starts in background goroutine - if srvErr := activateServer(ctx, opts, detector); srvErr != nil { + if srvErr := activateServer(ctx, opts, spamBot); srvErr != nil { return fmt.Errorf("can't activate web server, %w", srvErr) } + if opts.Telegram.Token == "" || opts.Telegram.Group == "" { + log.Printf("[WARN] no telegram token and group, web server only mode") + // if no telegram token and group set, just run the server + <-ctx.Done() + return nil + } } - // make spam bot - spamBot, err := makeSpamBot(ctx, opts, detector) + // make telegram bot + tbAPI, err := tbapi.NewBotAPI(opts.Telegram.Token) if err != nil { - return fmt.Errorf("can't make spam bot, %w", err) + return fmt.Errorf("can't make telegram bot, %w", err) } + tbAPI.Debug = opts.TGDbg + + go autoSaveApprovedUsers(ctx, detector, approvedUsersStore, time.Minute*5) // make spam logger loggerWr, err := makeSpamLogWriter(opts) @@ -251,7 +262,7 @@ func execute(ctx context.Context, opts options) error { return nil } -func activateServer(ctx context.Context, opts options, detector *lib.Detector) (err error) { +func activateServer(ctx context.Context, opts options, spamFilter *bot.SpamFilter) (err error) { log.Printf("[INFO] start web server on %s", opts.Server.ListenAddr) authPassswd := opts.Server.AuthPasswd if opts.Server.AuthPasswd == "auto" { @@ -259,12 +270,12 @@ func activateServer(ctx context.Context, opts options, detector *lib.Detector) ( if err != nil { return fmt.Errorf("can't generate random password, %w", err) } - log.Printf("[WARN] basic auth password for user 'tg-spam': %s", authPassswd) + log.Printf("[WARN] generated basic auth password for user tg-spam: %q", authPassswd) } srv := webapi.Server{Config: webapi.Config{ ListenAddr: opts.Server.ListenAddr, - Detector: detector, + SpamFilter: spamFilter.Detector, AuthPasswd: authPassswd, Version: revision, Dbg: opts.Dbg, @@ -279,6 +290,8 @@ func activateServer(ctx context.Context, opts options, detector *lib.Detector) ( return nil } +// makeDetector creates spam detector with all checkers and updaters +// it loads samples and dynamic files func makeDetector(opts options) *lib.Detector { detectorConfig := lib.Config{ MaxAllowedEmoji: opts.MaxEmoji, diff --git a/app/main_test.go b/app/main_test.go index 80756f7..cfc17ec 100644 --- a/app/main_test.go +++ b/app/main_test.go @@ -213,17 +213,21 @@ func Test_makeSpamBot(t *testing.T) { }) } -func Test_activateServer(t *testing.T) { +func Test_activateServerOnly(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() var opts options opts.Server.Enabled = true opts.Server.ListenAddr = ":9988" + opts.Server.AuthPasswd = "auto" + opts.Files.SamplesDataPath = "webapi/testdata" + opts.Files.DynamicDataPath = "webapi/testdata" done := make(chan struct{}) go func() { - activateServer(ctx, opts, makeDetector(opts)) + err := execute(ctx, opts) + assert.NoError(t, err) close(done) }() time.Sleep(time.Millisecond * 100) @@ -235,5 +239,6 @@ func Test_activateServer(t *testing.T) { body, err := io.ReadAll(resp.Body) require.NoError(t, err) assert.Equal(t, "pong", string(body)) + cancel() <-done } diff --git a/app/webapi/mocks/detector.go b/app/webapi/mocks/detector.go index 3cce689..72a2a54 100644 --- a/app/webapi/mocks/detector.go +++ b/app/webapi/mocks/detector.go @@ -8,11 +8,11 @@ import ( "sync" ) -// DetectorMock is a mock implementation of webapi.Detector. +// DetectorMock is a mock implementation of webapi.SpamFilter. // // func TestSomethingThatUsesDetector(t *testing.T) { // -// // make and configure a mocked webapi.Detector +// // make and configure a mocked webapi.SpamFilter // mockedDetector := &DetectorMock{ // AddApprovedUsersFunc: func(ids ...string) { // panic("mock out the AddApprovedUsers method") @@ -34,7 +34,7 @@ import ( // }, // } // -// // use mockedDetector in code that requires webapi.Detector +// // use mockedDetector in code that requires webapi.SpamFilter // // and then make assertions. // // } @@ -101,7 +101,7 @@ type DetectorMock struct { // AddApprovedUsers calls AddApprovedUsersFunc. func (mock *DetectorMock) AddApprovedUsers(ids ...string) { if mock.AddApprovedUsersFunc == nil { - panic("DetectorMock.AddApprovedUsersFunc: method is nil but Detector.AddApprovedUsers was just called") + panic("DetectorMock.AddApprovedUsersFunc: method is nil but SpamFilter.AddApprovedUsers was just called") } callInfo := struct { Ids []string @@ -140,7 +140,7 @@ func (mock *DetectorMock) ResetAddApprovedUsersCalls() { // ApprovedUsers calls ApprovedUsersFunc. func (mock *DetectorMock) ApprovedUsers() []string { if mock.ApprovedUsersFunc == nil { - panic("DetectorMock.ApprovedUsersFunc: method is nil but Detector.ApprovedUsers was just called") + panic("DetectorMock.ApprovedUsersFunc: method is nil but SpamFilter.ApprovedUsers was just called") } callInfo := struct { }{} @@ -174,7 +174,7 @@ func (mock *DetectorMock) ResetApprovedUsersCalls() { // Check calls CheckFunc. func (mock *DetectorMock) Check(msg string, userID string) (bool, []lib.CheckResult) { if mock.CheckFunc == nil { - panic("DetectorMock.CheckFunc: method is nil but Detector.Check was just called") + panic("DetectorMock.CheckFunc: method is nil but SpamFilter.Check was just called") } callInfo := struct { Msg string @@ -217,7 +217,7 @@ func (mock *DetectorMock) ResetCheckCalls() { // RemoveApprovedUsers calls RemoveApprovedUsersFunc. func (mock *DetectorMock) RemoveApprovedUsers(ids ...string) { if mock.RemoveApprovedUsersFunc == nil { - panic("DetectorMock.RemoveApprovedUsersFunc: method is nil but Detector.RemoveApprovedUsers was just called") + panic("DetectorMock.RemoveApprovedUsersFunc: method is nil but SpamFilter.RemoveApprovedUsers was just called") } callInfo := struct { Ids []string @@ -256,7 +256,7 @@ func (mock *DetectorMock) ResetRemoveApprovedUsersCalls() { // UpdateHam calls UpdateHamFunc. func (mock *DetectorMock) UpdateHam(msg string) error { if mock.UpdateHamFunc == nil { - panic("DetectorMock.UpdateHamFunc: method is nil but Detector.UpdateHam was just called") + panic("DetectorMock.UpdateHamFunc: method is nil but SpamFilter.UpdateHam was just called") } callInfo := struct { Msg string @@ -295,7 +295,7 @@ func (mock *DetectorMock) ResetUpdateHamCalls() { // UpdateSpam calls UpdateSpamFunc. func (mock *DetectorMock) UpdateSpam(msg string) error { if mock.UpdateSpamFunc == nil { - panic("DetectorMock.UpdateSpamFunc: method is nil but Detector.UpdateSpam was just called") + panic("DetectorMock.UpdateSpamFunc: method is nil but SpamFilter.UpdateSpam was just called") } callInfo := struct { Msg string diff --git a/app/webapi/testdata/ham-samples.txt b/app/webapi/testdata/ham-samples.txt new file mode 100644 index 0000000..99f6d87 --- /dev/null +++ b/app/webapi/testdata/ham-samples.txt @@ -0,0 +1 @@ +this is ham, good ham, and it’s not going to be wasted. I’m going to eat it, and I’m going to enjoy it.” And I did. And I did. \ No newline at end of file diff --git a/app/webapi/testdata/spam-samples.txt b/app/webapi/testdata/spam-samples.txt new file mode 100644 index 0000000..1d21092 --- /dev/null +++ b/app/webapi/testdata/spam-samples.txt @@ -0,0 +1 @@ +this is spam, please ignore it. If you are a human, ignore it. \ No newline at end of file diff --git a/app/webapi/webapi.go b/app/webapi/webapi.go index e448cb6..2627fc6 100644 --- a/app/webapi/webapi.go +++ b/app/webapi/webapi.go @@ -24,7 +24,7 @@ import ( "github.com/umputun/tg-spam/lib" ) -//go:generate moq --out mocks/detector.go --pkg mocks --with-resets --skip-ensure . Detector +//go:generate moq --out mocks/spam_filter.go --pkg mocks --with-resets --skip-ensure . SpamFilter // Server is a web API server. type Server struct { @@ -33,15 +33,15 @@ type Server struct { // Config defines server parameters type Config struct { - Version string // version to show in /ping - ListenAddr string // listen address - Detector Detector // spam detector - AuthPasswd string // basic auth password for user "tg-spam" - Dbg bool // debug mode + Version string // version to show in /ping + ListenAddr string // listen address + SpamFilter SpamFilter // spam detector + AuthPasswd string // basic auth password for user "tg-spam" + Dbg bool // debug mode } -// Detector is a spam detector interface. -type Detector interface { +// SpamFilter is a spam detector interface. +type SpamFilter interface { Check(msg string, userID string) (spam bool, cr []lib.CheckResult) UpdateSpam(msg string) error UpdateHam(msg string) error @@ -97,14 +97,14 @@ func (s *Server) routes(router *chi.Mux) *chi.Mux { router.Post("/check", s.checkHandler) // check a message for spam router.Route("/update", func(r chi.Router) { // update spam/ham samples - r.Post("/spam", s.updateSampleHandler(s.Detector.UpdateSpam)) // update spam samples - r.Post("/ham", s.updateSampleHandler(s.Detector.UpdateHam)) // update ham samples + r.Post("/spam", s.updateSampleHandler(s.SpamFilter.UpdateSpam)) // update spam samples + r.Post("/ham", s.updateSampleHandler(s.SpamFilter.UpdateHam)) // update ham samples }) router.Route("/users", func(r chi.Router) { // manage approved users - r.Post("/", s.updateApprovedUsersHandler(s.Detector.AddApprovedUsers)) // add user to the approved list - r.Delete("/", s.updateApprovedUsersHandler(s.Detector.RemoveApprovedUsers)) // remove user from approved list - r.Get("/", s.getApprovedUsersHandler) // get approved users + r.Post("/", s.updateApprovedUsersHandler(s.SpamFilter.AddApprovedUsers)) // add user to the approved list + r.Delete("/", s.updateApprovedUsersHandler(s.SpamFilter.RemoveApprovedUsers)) // remove user from approved list + r.Get("/", s.getApprovedUsersHandler) // get approved users }) return router } @@ -123,7 +123,7 @@ func (s *Server) checkHandler(w http.ResponseWriter, r *http.Request) { return } - spam, cr := s.Detector.Check(req.Msg, req.UserID) + spam, cr := s.SpamFilter.Check(req.Msg, req.UserID) rest.RenderJSON(w, rest.JSON{"spam": spam, "checks": cr}) } @@ -171,7 +171,7 @@ func (s *Server) updateApprovedUsersHandler(updFn func(ids ...string)) func(w ht // getApprovedUsersHandler handles GET /users request. It returns list of approved users. func (s *Server) getApprovedUsersHandler(w http.ResponseWriter, _ *http.Request) { - rest.RenderJSON(w, rest.JSON{"user_ids": s.Detector.ApprovedUsers()}) + rest.RenderJSON(w, rest.JSON{"user_ids": s.SpamFilter.ApprovedUsers()}) } // GenerateRandomPassword generates a random password of a given length diff --git a/app/webapi/webapi_test.go b/app/webapi/webapi_test.go index 288e950..d053019 100644 --- a/app/webapi/webapi_test.go +++ b/app/webapi/webapi_test.go @@ -22,7 +22,7 @@ func TestServer_Run(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - srv := NewServer(Config{ListenAddr: ":9876", Version: "dev", Detector: &mocks.DetectorMock{}}) + srv := NewServer(Config{ListenAddr: ":9876", Version: "dev", SpamFilter: &mocks.DetectorMock{}}) done := make(chan struct{}) go func() { err := srv.Run(ctx) @@ -56,7 +56,7 @@ func TestServer_RunAuth(t *testing.T) { }, } - srv := NewServer(Config{ListenAddr: ":9877", Version: "dev", Detector: mockDetector, AuthPasswd: "test"}) + srv := NewServer(Config{ListenAddr: ":9877", Version: "dev", SpamFilter: mockDetector, AuthPasswd: "test"}) done := make(chan struct{}) go func() { err := srv.Run(ctx) @@ -137,7 +137,7 @@ func TestServer_routes(t *testing.T) { return []string{"user1", "user2"} }, } - server := NewServer(Config{Detector: mockDetector}) + server := NewServer(Config{SpamFilter: mockDetector}) ts := httptest.NewServer(server.routes(chi.NewRouter())) defer ts.Close() @@ -243,8 +243,8 @@ func TestServer_checkHandler(t *testing.T) { }, } server := NewServer(Config{ - Detector: mockDetector, - Version: "1.0", + SpamFilter: mockDetector, + Version: "1.0", }) t.Run("spam", func(t *testing.T) { @@ -328,7 +328,7 @@ func TestServer_updateHandler(t *testing.T) { return nil }, } - server := NewServer(Config{Detector: mockDetector}) + server := NewServer(Config{SpamFilter: mockDetector}) t.Run("successful update ham", func(t *testing.T) { mockDetector.ResetCalls() @@ -404,7 +404,7 @@ func TestServer_updateApprovedUsersHandler(t *testing.T) { } }, } - server := NewServer(Config{Detector: mockDetector}) + server := NewServer(Config{SpamFilter: mockDetector}) t.Run("successful update", func(t *testing.T) { mockDetector.ResetCalls() diff --git a/lib/lib_test.go b/lib/lib_test.go index c157492..20c1d79 100644 --- a/lib/lib_test.go +++ b/lib/lib_test.go @@ -11,7 +11,7 @@ import ( // ExampleNewDetector demonstrates how to initialize a new Detector and use it to check a message for spam. func ExampleNewDetector() { - // Initialize a new Detector with a Config + // Initialize a new SpamFilter with a Config detector := lib.NewDetector(lib.Config{ MaxAllowedEmoji: 5, MinMsgLen: 10,