Skip to content

Commit

Permalink
Add from detected spam (#55)
Browse files Browse the repository at this point in the history
* add ability to add detected spam to samples

this is needed if spam detected by heuristic method, like number of emojis or links but failed to be detected by classifier. in this case it makes sense to add it to dynamic spam samples directly from webui

* render error on spam detection page

* don't convert to int64 separately
  • Loading branch information
umputun authored Feb 1, 2024
1 parent 00234ee commit 8f1a88c
Show file tree
Hide file tree
Showing 11 changed files with 257 additions and 74 deletions.
18 changes: 9 additions & 9 deletions app/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -363,15 +363,15 @@ func activateServer(ctx context.Context, opts options, sf *bot.SpamFilter, loc *
}

srv := webapi.Server{Config: webapi.Config{
ListenAddr: opts.Server.ListenAddr,
Detector: sf.Detector,
SpamFilter: sf,
Locator: loc,
DetectedSpamReader: detectedSpamStore,
AuthPasswd: authPassswd,
Version: revision,
Dbg: opts.Dbg,
Settings: settings,
ListenAddr: opts.Server.ListenAddr,
Detector: sf.Detector,
SpamFilter: sf,
Locator: loc,
DetectedSpam: detectedSpamStore,
AuthPasswd: authPassswd,
Version: revision,
Dbg: opts.Dbg,
Settings: settings,
}}

go func() {
Expand Down
35 changes: 27 additions & 8 deletions app/storage/detected_spam.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"log"
"strings"
"time"

"github.com/jmoiron/sqlx"
Expand All @@ -20,28 +21,37 @@ type DetectedSpam struct {

// DetectedSpamInfo represents information about a detected spam entry.
type DetectedSpamInfo struct {
ID int64 `db:"id"`
Text string `db:"text"`
UserID int64 `db:"user_id"`
UserName string `db:"user_name"`
Timestamp time.Time `db:"timestamp"`
Added bool `db:"added"` // added to samples
ChecksJSON string `db:"checks"` // Store as JSON
Checks []spamcheck.Response `db:"-"` // Don't store in DB
}

// NewDetectedSpam creates a new DetectedSpam storage
func NewDetectedSpam(db *sqlx.DB) (*DetectedSpam, error) {
_, err := db.Exec(`CREATE TABLE IF NOT EXISTS detected_spam (
id INTEGER PRIMARY KEY AUTOINCREMENT,
text TEXT,
user_id INTEGER,
user_name TEXT,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
checks TEXT
)`)
id INTEGER PRIMARY KEY AUTOINCREMENT,
text TEXT,
user_id INTEGER,
user_name TEXT,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
added BOOLEAN DEFAULT 0,
checks TEXT
)`)
if err != nil {
return nil, fmt.Errorf("failed to create detected_spam table: %w", err)
}

_, err = db.Exec(`ALTER TABLE detected_spam ADD COLUMN added BOOLEAN DEFAULT 0`)
if err != nil {
if !strings.Contains(err.Error(), "duplicate column name") {
return nil, fmt.Errorf("failed to alter detected_spam table: %w", err)
}
}
// add index on timestamp
if _, err = db.Exec(`CREATE INDEX IF NOT EXISTS idx_detected_spam_timestamp ON detected_spam(timestamp)`); err != nil {
return nil, fmt.Errorf("failed to create index on timestamp: %w", err)
Expand All @@ -66,10 +76,19 @@ func (ds *DetectedSpam) Write(entry DetectedSpamInfo, checks []spamcheck.Respons
return nil
}

// SetAddedToSamplesFlag sets the added flag to true for the detected spam entry with the given id
func (ds *DetectedSpam) SetAddedToSamplesFlag(id int64) error {
query := `UPDATE detected_spam SET added = 1 WHERE id = ?`
if _, err := ds.db.Exec(query, id); err != nil {
return fmt.Errorf("failed to update added to samples flag: %w", err)
}
return nil
}

// Read returns all detected spam entries
func (ds *DetectedSpam) Read() ([]DetectedSpamInfo, error) {
var entries []DetectedSpamInfo
err := ds.db.Select(&entries, "SELECT text, user_id, user_name, timestamp, checks FROM detected_spam ORDER BY timestamp DESC LIMIT ?", maxDetectedSpamEntries)
err := ds.db.Select(&entries, "SELECT * FROM detected_spam ORDER BY timestamp DESC LIMIT ?", maxDetectedSpamEntries)
if err != nil {
return nil, fmt.Errorf("failed to get detected spam entries: %w", err)
}
Expand Down
38 changes: 38 additions & 0 deletions app/storage/detected_spam_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,44 @@ func TestDetectedSpam_Write(t *testing.T) {
assert.Equal(t, 1, count)
}

func TestSetAddedToSamplesFlag(t *testing.T) {
db, err := sqlx.Open("sqlite", ":memory:")
require.NoError(t, err)
defer db.Close()

ds, err := NewDetectedSpam(db)
require.NoError(t, err)

spamEntry := DetectedSpamInfo{
Text: "spam message",
UserID: 1,
UserName: "Spammer",
Timestamp: time.Now(),
}

checks := []spamcheck.Response{
{
Name: "Check1",
Spam: true,
Details: "Details 1",
},
}

err = ds.Write(spamEntry, checks)
require.NoError(t, err)
var added bool
err = db.Get(&added, "SELECT added FROM detected_spam WHERE text = ?", spamEntry.Text)
require.NoError(t, err)
assert.False(t, added)

err = ds.SetAddedToSamplesFlag(1)
require.NoError(t, err)

err = db.Get(&added, "SELECT added FROM detected_spam WHERE text = ?", spamEntry.Text)
require.NoError(t, err)
assert.True(t, added)
}

func TestDetectedSpam_Read(t *testing.T) {
db, err := sqlx.Open("sqlite", ":memory:")
require.NoError(t, err)
Expand Down
4 changes: 4 additions & 0 deletions app/webapi/assets/components/heads.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.5.0/font/bootstrap-icons.css" rel="stylesheet">
<script src="https://unpkg.com/htmx.org"></script>
<link href="styles.css" rel="stylesheet">
25 changes: 19 additions & 6 deletions app/webapi/assets/detected_spam.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,15 @@
<html>
<head>
<title>Detected Spam - TG-Spam</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.5.0/font/bootstrap-icons.css" rel="stylesheet">
<script src="https://unpkg.com/htmx.org"></script>
<link href="styles.css" rel="stylesheet">
{{template "heads.html"}}
</head>
<body>
{{template "navbar.html"}}

<div class="container mt-4">
<div class="row" id="spam-list">
<div class="col-md-12">
<div id="error-message" class="alert alert-danger d-none" role="alert"></div>
<h4>Detected Spam ({{.TotalDetectedSpam}})</h4>
<table class="table table-striped">
<thead class="custom-table-header">
Expand All @@ -26,17 +24,32 @@ <h4>Detected Spam ({{.TotalDetectedSpam}})</h4>
</thead>
<tbody>
{{range .DetectedSpamEntries}}
{{$text := .Text}}
{{$id := .ID}}
{{$added := .Added}}
<tr>
<td class="ds-timestamp">{{.Timestamp.Format "2006-01-02 15:04:05"}}</td>
<td>{{.UserID}}</td>
<td>{{.UserName}}</td>
<td class="ds-text">{{.Text}}</td>
<td class="ds-checks">
{{range .Checks}}
<div class="{{if .Spam}}text-danger{{else}}text-success{{end}}">
<strong>{{.Name}}:</strong> {{.Details}}
<div style="display: flex; align-items: center;">
<div class="{{if .Spam}}text-danger{{else}}text-success{{end}}" style="flex-grow: 1;">
<strong>{{.Name}}:</strong> {{.Details}}
</div>
{{if and (not .Spam) (not $added) (eq .Name "classifier")}}
<button
hx-post="/detected_spam/add" hx-vals='{"id": {{$id}}, "msg": "{{$text}}"}'
hx-target="#error-message" hx-error="#error-message" hx-swap="outerHTML"
class="btn btn-sm btn-warning"
title="Add this message to spam samples">
Add
</button>
{{end}}
</div>
{{end}}
</td>
</tr>
{{else}}
<tr>
Expand Down
5 changes: 1 addition & 4 deletions app/webapi/assets/manage_samples.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@
<html>
<head>
<title>Manage Samples - TG-Spam</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.5.0/font/bootstrap-icons.css" rel="stylesheet">
<script src="https://unpkg.com/htmx.org"></script>
<link href="styles.css" rel="stylesheet">
{{template "heads.html"}}
</head>
<body>
{{template "navbar.html"}}
Expand Down
5 changes: 1 addition & 4 deletions app/webapi/assets/manage_users.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@
<html>
<head>
<title>Manage Users - TG-Spam</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.5.0/font/bootstrap-icons.css" rel="stylesheet">
<script src="https://unpkg.com/htmx.org"></script>
<link href="styles.css" rel="stylesheet">
{{template "heads.html"}}
</head>
<body>
{{template "navbar.html"}}
Expand Down
4 changes: 1 addition & 3 deletions app/webapi/assets/settings.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
<html>
<head>
<title>Settings - TG-Spam</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.5.0/font/bootstrap-icons.css" rel="stylesheet">
<link href="styles.css" rel="stylesheet">
{{template "heads.html"}}
</head>
<body>

Expand Down
81 changes: 68 additions & 13 deletions app/webapi/mocks/detected_spam.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 8f1a88c

Please sign in to comment.