feature: switch domaincheckbot to sqlite kv

This commit is contained in:
Seraphim Strub 2024-07-12 13:51:36 +00:00
parent 50cd000ee5
commit 773318a079
3 changed files with 48 additions and 133 deletions

View file

@ -1,21 +1,25 @@
package dns package dns
import ( import (
"grow.rievo.dev/discordBots/cmd/domaincheckbot/repository"
"net" "net"
"sort" "sort"
) )
func CheckDomain(domain string) repository.Domain { type Domain struct {
Name string
NS []string
}
func CheckDomain(domain string) Domain {
nameservers, err := net.LookupNS(domain) nameservers, err := net.LookupNS(domain)
if len(nameservers) > 0 && err == nil { if len(nameservers) > 0 && err == nil {
return repository.Domain{ return Domain{
Name: domain, Name: domain,
NS: nsToArray(nameservers), NS: nsToArray(nameservers),
} }
} }
return repository.Domain{ return Domain{
Name: domain, Name: domain,
NS: []string{}, NS: []string{},
} }

View file

@ -2,23 +2,27 @@ package main
import ( import (
"context" "context"
"database/sql"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"github.com/dgraph-io/badger/v4"
"github.com/disgoorg/disgo" "github.com/disgoorg/disgo"
"github.com/disgoorg/disgo/discord" "github.com/disgoorg/disgo/discord"
"github.com/disgoorg/disgo/rest" "github.com/disgoorg/disgo/rest"
"github.com/disgoorg/disgo/webhook" "github.com/disgoorg/disgo/webhook"
"github.com/disgoorg/snowflake/v2" "github.com/disgoorg/snowflake/v2"
"grow.rievo.dev/discordBots"
"grow.rievo.dev/discordBots/cmd/domaincheckbot/config" "grow.rievo.dev/discordBots/cmd/domaincheckbot/config"
"grow.rievo.dev/discordBots/cmd/domaincheckbot/dns" "grow.rievo.dev/discordBots/cmd/domaincheckbot/dns"
"grow.rievo.dev/discordBots/cmd/domaincheckbot/repository" "grow.rievo.dev/discordBots/pkg/db"
"log/slog" "log/slog"
"os" "os"
"os/signal" "os/signal"
"reflect" "reflect"
"syscall" "syscall"
"time" "time"
_ "modernc.org/sqlite"
) )
var ( var (
@ -39,11 +43,22 @@ func main() {
client := webhook.New(webhookID, webhookToken) client := webhook.New(webhookID, webhookToken)
defer client.Close(context.TODO()) defer client.Close(context.TODO())
repo := repository.InitDb() ctx := context.Background()
defer repo.Close() con, err := sql.Open("sqlite", "file:db/domain.db?cache=shared")
if err != nil {
logger.Error("error opening db", slog.Any("error", err))
panic(err)
}
defer con.Close()
ticker := time.NewTicker(10 * time.Minute) // create tables
tickerGC := time.NewTicker(15 * time.Minute) if _, err := con.ExecContext(ctx, discordBots.Schema); err != nil {
logger.Error("error creating db schema", slog.Any("error", err))
panic(err)
}
query := db.New(con)
ticker := time.NewTicker(1 * time.Minute)
quit := make(chan struct{}) quit := make(chan struct{})
go func() { go func() {
@ -51,20 +66,11 @@ func main() {
select { select {
case <-ticker.C: case <-ticker.C:
for _, d := range config.Domains { for _, d := range config.Domains {
go checkDomain(0, d, repo, client) go checkDomain(0, d, query, client)
}
case <-tickerGC.C:
err := repo.RunGC()
if err != nil && !errors.Is(err, badger.ErrNoRewrite) {
logger.Error("GC failed", slog.Any("error", err))
} else {
logger.Debug("GC successful")
} }
case <-quit: case <-quit:
ticker.Stop() ticker.Stop()
tickerGC.Stop()
return return
} }
} }
@ -77,9 +83,13 @@ func main() {
<-s <-s
} }
func checkDomain(counter int, d string, repo *repository.DomainRepository, client webhook.Client) { func checkDomain(counter int, d string, query *db.Queries, client webhook.Client) {
domain := dns.CheckDomain(d) domain := dns.CheckDomain(d)
retrievedDomain, _ := repo.GetValue(d) retrievedDomainJson, dbErr := query.GetItem(context.TODO(), d)
retrievedDomain := dns.Domain{}
if err := json.Unmarshal(retrievedDomainJson.Data, &retrievedDomain); !errors.Is(dbErr, sql.ErrNoRows) && err != nil {
logger.Error("failed unmarshalling deal", slog.Any("error", err))
}
if reflect.DeepEqual(domain, retrievedDomain) { if reflect.DeepEqual(domain, retrievedDomain) {
logger.Debug("domain did not change", slog.String("domain", d)) logger.Debug("domain did not change", slog.String("domain", d))
return return
@ -88,14 +98,23 @@ func checkDomain(counter int, d string, repo *repository.DomainRepository, clien
counter += 1 counter += 1
if counter >= 2 { if counter >= 2 {
go sendWebhook(client, domain, retrievedDomain) go sendWebhook(client, domain, retrievedDomain)
repo.SetValue(domain)
domainJson, _ := json.Marshal(domain)
err := query.CreateItem(context.TODO(), db.CreateItemParams{
ID: domain.Name,
Data: domainJson,
})
if err != nil {
logger.Error("failed saving domain", slog.Any("error", err))
}
return return
} }
time.Sleep(1 * time.Minute) time.Sleep(1 * time.Minute)
checkDomain(counter, d, repo, client) checkDomain(counter, d, query, client)
} }
func sendWebhook(client webhook.Client, domain repository.Domain, oldDomain repository.Domain) { func sendWebhook(client webhook.Client, domain dns.Domain, oldDomain dns.Domain) {
var status string var status string
status = fmt.Sprintf("```md\n# %v", domain.Name) status = fmt.Sprintf("```md\n# %v", domain.Name)

View file

@ -1,108 +0,0 @@
package repository
import (
"encoding/json"
"github.com/dgraph-io/badger/v4"
"log"
)
type Domain struct {
Name string
NS []string
}
type Repository interface {
GetAll() ([]Domain, error)
GetValue(domainName string) Domain
SetValue(domain Domain) error
DeleteValue(domainName string) error
Close() error
}
type DomainRepository struct {
db *badger.DB
}
func InitDb() *DomainRepository {
opts := badger.DefaultOptions("./badger")
opts.Logger = nil
db, err := badger.Open(opts)
if err != nil {
log.Fatal(err)
}
return &DomainRepository{db}
}
func (d *DomainRepository) Close() error {
return d.db.Close()
}
func (d *DomainRepository) RunGC() error {
return d.db.RunValueLogGC(0.7)
}
func (d *DomainRepository) GetAll() ([]Domain, error) {
var domains []Domain
err := d.db.View(func(txn *badger.Txn) error {
opts := badger.DefaultIteratorOptions
opts.PrefetchSize = 10
it := txn.NewIterator(opts)
defer it.Close()
for it.Rewind(); it.Valid(); it.Next() {
item := it.Item()
err := item.Value(func(val []byte) error {
retrievedDomain := Domain{}
err := json.Unmarshal(val, &retrievedDomain)
domains = append(domains, retrievedDomain)
return err
})
if err != nil {
return err
}
}
return nil
})
return domains, err
}
func (d *DomainRepository) GetValue(domainName string) (Domain, error) {
retrievedDomain := Domain{}
err := d.db.View(func(txn *badger.Txn) error {
item, err := txn.Get([]byte(domainName))
if err != nil {
return err
}
err = item.Value(func(val []byte) error {
err = json.Unmarshal(val, &retrievedDomain)
return err
})
return err
})
if err != nil {
return Domain{}, err
}
return retrievedDomain, nil
}
func (d *DomainRepository) SetValue(domain Domain) error {
jsonBytes, err := json.Marshal(domain)
if err != nil {
return err
}
err = d.db.Update(func(txn *badger.Txn) error {
err := txn.Set([]byte(domain.Name), jsonBytes)
return err
})
if err != nil {
return err
}
return nil
}
func (d *DomainRepository) DeleteValue(domainName string) error {
err := d.db.Update(func(txn *badger.Txn) error {
err := txn.Delete([]byte(domainName))
return err
})
return err
}