2022-05-31 15:19:37 +00:00
|
|
|
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)
|
2022-10-25 17:11:15 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Error().Err(err).Msg("findAlias")
|
|
|
|
}
|
2022-05-31 15:19:37 +00:00
|
|
|
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
|
|
|
|
}
|