catbase/plugins/fact/fact.go

242 lines
4.3 KiB
Go

package fact
import (
"database/sql"
"fmt"
"github.com/jmoiron/sqlx"
"github.com/rs/zerolog/log"
"time"
)
// Factoid stores info about our factoid for lookup and later interaction
type Factoid struct {
ID sql.NullInt64
Fact string
Tidbit string
Verb string
Owner string
Created time.Time
Accessed time.Time
Count int
}
type alias struct {
Fact string
Next string
}
func (a *alias) resolve(db *sqlx.DB) (*Factoid, error) {
// perform db query to fill the To field
q := `select fact, next from factoid_alias where fact=?`
var next alias
err := db.Get(&next, q, a.Next)
if err != nil {
// we hit the end of the chain, get a factoid named Next
fact, err := GetSingleFact(db, a.Next)
if err != nil {
err := fmt.Errorf("Error resolvig alias %v: %v", a, err)
return nil, err
}
return fact, nil
}
return next.resolve(db)
}
func findAlias(db *sqlx.DB, fact string) (bool, *Factoid) {
q := `select * from factoid_alias where fact=?`
var a alias
err := db.Get(&a, q, fact)
if err != nil {
return false, nil
}
f, err := a.resolve(db)
return err == nil, f
}
func (a *alias) save(db *sqlx.DB) error {
q := `select * from factoid_alias where fact=?`
var offender alias
err := db.Get(&offender, q, a.Next)
if err == nil {
return fmt.Errorf("DANGER: an opposite alias already exists")
}
_, err = a.resolve(db)
if err != nil {
return fmt.Errorf("there is no fact at that destination")
}
q = `insert or replace into factoid_alias (fact, next) values (?, ?)`
_, err = db.Exec(q, a.Fact, a.Next)
if err != nil {
return err
}
return nil
}
func aliasFromStrings(from, to string) *alias {
return &alias{from, to}
}
func (f *Factoid) Save(db *sqlx.DB) error {
var err error
if f.ID.Valid {
// update
_, err = db.Exec(`update factoid set
fact=?,
tidbit=?,
verb=?,
owner=?,
accessed=?,
count=?
where id=?`,
f.Fact,
f.Tidbit,
f.Verb,
f.Owner,
f.Accessed.Unix(),
f.Count,
f.ID.Int64)
} else {
f.Created = time.Now()
f.Accessed = time.Now()
// insert
res, err := db.Exec(`insert into factoid (
fact,
tidbit,
verb,
owner,
created,
accessed,
count
) values (?, ?, ?, ?, ?, ?, ?);`,
f.Fact,
f.Tidbit,
f.Verb,
f.Owner,
f.Created.Unix(),
f.Accessed.Unix(),
f.Count,
)
if err != nil {
return err
}
id, err := res.LastInsertId()
// hackhackhack?
f.ID.Int64 = id
f.ID.Valid = true
}
return err
}
func (f *Factoid) delete(db *sqlx.DB) error {
var err error
if f.ID.Valid {
_, err = db.Exec(`delete from factoid where id=?`, f.ID)
}
f.ID.Valid = false
return err
}
func getFacts(db *sqlx.DB, fact string, tidbit string) ([]*Factoid, error) {
var fs []*Factoid
query := `select
id,
fact,
tidbit,
verb,
owner,
created,
accessed,
count
from factoid
where fact like ?
and tidbit like ?;`
rows, err := db.Query(query,
"%"+fact+"%", "%"+tidbit+"%")
if err != nil {
log.Error().Err(err).Msg("Error regexping for facts")
return nil, err
}
for rows.Next() {
var f Factoid
var tmpCreated int64
var tmpAccessed int64
err := rows.Scan(
&f.ID,
&f.Fact,
&f.Tidbit,
&f.Verb,
&f.Owner,
&tmpCreated,
&tmpAccessed,
&f.Count,
)
if err != nil {
return nil, err
}
f.Created = time.Unix(tmpCreated, 0)
f.Accessed = time.Unix(tmpAccessed, 0)
fs = append(fs, &f)
}
return fs, err
}
func GetSingle(db *sqlx.DB) (*Factoid, error) {
var f Factoid
var tmpCreated int64
var tmpAccessed int64
err := db.QueryRow(`select
id,
fact,
tidbit,
verb,
owner,
created,
accessed,
count
from factoid
order by random() limit 1;`).Scan(
&f.ID,
&f.Fact,
&f.Tidbit,
&f.Verb,
&f.Owner,
&tmpCreated,
&tmpAccessed,
&f.Count,
)
f.Created = time.Unix(tmpCreated, 0)
f.Accessed = time.Unix(tmpAccessed, 0)
return &f, err
}
func GetSingleFact(db *sqlx.DB, fact string) (*Factoid, error) {
var f Factoid
var tmpCreated int64
var tmpAccessed int64
err := db.QueryRow(`select
id,
fact,
tidbit,
verb,
owner,
created,
accessed,
count
from factoid
where fact like ?
order by random() limit 1;`,
fact).Scan(
&f.ID,
&f.Fact,
&f.Tidbit,
&f.Verb,
&f.Owner,
&tmpCreated,
&tmpAccessed,
&f.Count,
)
f.Created = time.Unix(tmpCreated, 0)
f.Accessed = time.Unix(tmpAccessed, 0)
return &f, err
}