From 3d6c1a444f37895b8755ae090b53de9407cd04d4 Mon Sep 17 00:00:00 2001 From: Zorchenhimer Date: Sat, 13 Feb 2021 11:39:05 -0500 Subject: [PATCH] Initial commit Main structure is mostly figured out. Only a noun phrase is currently generated. --- .gitignore | 4 + Makefile | 16 ++ api/api.go | 2 + business/business.go | 29 ++++ business/generic.go | 241 +++++++++++++++++++++++++ cmd/generate.go | 56 ++++++ cmd/server.go | 18 ++ database/database.go | 76 ++++++++ database/sqlite.go | 325 ++++++++++++++++++++++++++++++++++ frontend/frontend.go | 45 +++++ frontend/home.go | 14 ++ go.mod | 8 + go.sum | 6 + models/adjective.go | 11 ++ models/noun.go | 42 +++++ models/verb.go | 28 +++ server.go | 87 ++++++++++ settings.go | 51 ++++++ word_lists.json | 405 +++++++++++++++++++++++++++++++++++++++++++ 19 files changed, 1464 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 api/api.go create mode 100644 business/business.go create mode 100644 business/generic.go create mode 100644 cmd/generate.go create mode 100644 cmd/server.go create mode 100644 database/database.go create mode 100644 database/sqlite.go create mode 100644 frontend/frontend.go create mode 100644 frontend/home.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 models/adjective.go create mode 100644 models/noun.go create mode 100644 models/verb.go create mode 100644 server.go create mode 100644 settings.go create mode 100644 word_lists.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9dc8d0f --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +bin/ +settings.json +*.sqlite +*.txt diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..616340a --- /dev/null +++ b/Makefile @@ -0,0 +1,16 @@ + +#SOURCES= *.go \ +# api/*.go \ +# business/*.go \ +# cmd/*.go \ +# database/*.go \ +# frontend/*.go \ +# models/*.go + +SOURCES=$(shell find . -type f -name "*.go") + +bin/server: bin/ $(SOURCES) + go build -o bin/server cmd/server.go + +bin/: + mkdir -p bin diff --git a/api/api.go b/api/api.go new file mode 100644 index 0000000..9270dc9 --- /dev/null +++ b/api/api.go @@ -0,0 +1,2 @@ +package api + diff --git a/business/business.go b/business/business.go new file mode 100644 index 0000000..420a78c --- /dev/null +++ b/business/business.go @@ -0,0 +1,29 @@ +package business + +import ( + "github.com/zorchenhimer/hacker-quotes/models" + //"github.com/zorchenhimer/hacker-quotes/database" +) + +type HackerQuotes interface { + // Random returns a completely randomized quote. + Random() (string, error) + + // Format returns a quote in the given format. + Format(format string) (string, error) + + // InitData populates the underlying database with data from the given json file. + InitData(filename string) error +} + +type Admin interface { + AddNoun(word models.Noun) error + AddVerb(word models.Verb) error + + RemoveNoun(word string) error + // Word is the indefinite form. + RemoveVerb(word string) error + + GetNouns() ([]models.Noun, error) + GetVerbs() ([]models.Verb, error) +} diff --git a/business/generic.go b/business/generic.go new file mode 100644 index 0000000..4d3818a --- /dev/null +++ b/business/generic.go @@ -0,0 +1,241 @@ +package business + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "math/rand" + "strings" + + "github.com/zorchenhimer/hacker-quotes/database" + "github.com/zorchenhimer/hacker-quotes/models" +) + +type generic struct { + db database.DB +} + +func NewGeneric(db database.DB) (HackerQuotes, error) { + return &generic{db: db}, nil +} + +func (g *generic) Random() (string, error) { + definite := rand.Int() % 2 == 0 + hasAdj := rand.Int() % 2 == 0 + plural := rand.Int() % 2 == 0 + + np, err := g.nounPhrase(definite, hasAdj, plural) + if err != nil { + return "", err + } + + //fmt.Printf("(%s) definite: %t; hasAdj: %t; plural: %t\n", np, definite, hasAdj, plural) + + sb := strings.Builder{} + sb.WriteString(np) + + return sb.String(), nil +} + +func (g *generic) Format(format string) (string, error) { + return "", fmt.Errorf("Not implemented") +} + +func (g *generic) nounPhrase(definite, hasAdj, plural bool) (string, error){ + adj := "" + var err error + if hasAdj { + adj, err = g.randomAdjective() + if err != nil { + return "", err + } + } + + noun, err := g.randomNoun(plural) + if err != nil { + return "", err + } + + phrase := adj + if phrase != "" { + phrase += " " + noun + } else { + phrase = noun + } + + if definite && !plural { + //fmt.Println("[nounPhrase] definite && !plural") + return "the " + phrase, nil + } + + if !plural { + //fmt.Println("[nounPhrase] !plural") + return ana(phrase), nil + } + + return phrase, nil +} + +func (g *generic) randomAdjective() (string, error) { + ids, err := g.db.GetAdjectiveIds() + if err != nil { + return "", fmt.Errorf("[adj] get IDs error: %v", err) + } + + if len(ids) <= 0 { + return "", fmt.Errorf("No adjective IDs returned from database") + } + + rid := int(rand.Int63n(int64(len(ids)))) + //fmt.Printf("[adj] len(ids): %d; rid: %d; %d\n", len(ids), rid, ids[rid]) + + adj, err := g.db.GetAdjective(ids[rid]) + if err != nil { + return "", fmt.Errorf("[adj] ID: %d; %v", ids[rid], err) + } + + return adj.Word, nil +} + +func (g *generic) randomNoun(plural bool) (string, error) { + ids, err := g.db.GetNounIds() + if err != nil { + return "", fmt.Errorf("[noun] get IDs error: %v", err) + } + + if len(ids) <= 0 { + return "", fmt.Errorf("No noun IDs returned from database") + } + + rid := int(rand.Int63n(int64(len(ids)))) + //fmt.Printf("[noun] len(ids): %d; rid: %d; ID: %d\n", len(ids), rid, ids[rid]) + + noun, err := g.db.GetNoun(ids[rid]) + if err != nil { + return "", fmt.Errorf("[noun] ID: %d; %v", ids[rid], err) + } + + if plural { + return noun.Plural(), nil + } + return noun.Word, nil +} + +func (g *generic) randomVerb() (string, error) { + ids, err := g.db.GetVerbIds() + if err != nil { + return "", fmt.Errorf("[verb] get IDs error: %v", err) + } + + if len(ids) <= 0 { + return "", fmt.Errorf("No verb IDs returned from database") + } + + rid := int(rand.Int63n(int64(len(ids)))) + verb, err := g.db.GetVerb(ids[rid]) + if err != nil { + return "", fmt.Errorf("[verb] ID: %d; %v", ids[rid], err) + } + + return verb.Word, nil +} + +func (g *generic) InitData(filename string) error { + fmt.Printf("Initializing database with data in %q\n", filename) + if g.db == nil { + return fmt.Errorf("databse is nil!") + } + + raw, err := ioutil.ReadFile(filename) + if err != nil { + return err + } + + data := map[string][][]string{} + if err = json.Unmarshal(raw, &data); err != nil { + return err + } + + rawadj, ok := data["adjectives"] + if !ok { + return fmt.Errorf("Missing adjectives key in data") + } + + + adjectives := []models.Adjective{} + for _, adj := range rawadj { + t, word := adj[0], adj[1] + a := models.Adjective{Word: word} + if strings.Contains(t, "a") { + a.Absolute = true + } + if strings.Contains(t, "e") { + a.AppendEst = true + } + if strings.Contains(t, "m") { + a.AppendMore = true + } + + adjectives = append(adjectives, a) + } + + rawnoun, ok := data["nouns"] + if !ok { + return fmt.Errorf("Missing nouns key in data") + } + + nouns := []models.Noun{} + for _, noun := range rawnoun { + t, word := noun[0], noun[1] + n := models.Noun{Word: word} + + if strings.Contains(t, "m") { + n.Multiple = true + } + + if strings.Contains(t, "b") { + n.Begin = true + } + + if strings.Contains(t, "e") { + n.End = true + } + + if strings.Contains(t, "a") { + n.Alone = true + } + + if strings.Contains(t, "r") { + n.Regular = true + } + + nouns = append(nouns, n) + } + + rawverbs, ok := data["verbs"] + if !ok { + return fmt.Errorf("Missing verbs key in data") + } + + verbs := []models.Verb{} + for _, word := range rawverbs { + v := models.Verb{Word: word[1]} + if strings.Contains(word[0], "r") { + v.Regular = true + } + + verbs = append(verbs, v) + } + + return g.db.InitData(adjectives, nouns, verbs, nil) +} + +// Prepend "a", "an" or nothing to a phrase +func ana(phrase string) string { + //fmt.Printf("[ana] phrase[0]: %s; %q\n", string(phrase[0]), phrase) + if strings.ContainsAny(string(phrase[0]), "aeiou") { + return "an " + phrase + } + + return "a " + phrase +} diff --git a/cmd/generate.go b/cmd/generate.go new file mode 100644 index 0000000..cf8627d --- /dev/null +++ b/cmd/generate.go @@ -0,0 +1,56 @@ +package main + +import ( + "os" + "fmt" + + "github.com/zorchenhimer/hacker-quotes/business" + "github.com/zorchenhimer/hacker-quotes/database" +) + +func main() { + fmt.Println("len(os.Args):", len(os.Args)) + + var count int = 1 + if len(os.Args) == 2 { + fmt.Sscanf(os.Args[1], "%d", &count) + } + + db, err := database.New("sqlite", "file:db.sqlite?mode=memory") + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + hq, err := business.NewGeneric(db) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + err = hq.InitData("word_lists.json") + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + if count == 1 { + sentence, err := hq.Random() + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + fmt.Println("\n>>", sentence, "<<\n") + } else { + fmt.Println("") + for i := 0; i < count; i++ { + sentence, err := hq.Random() + if err != nil { + fmt.Println(err) + os.Exit(1) + } + fmt.Println(">>", sentence, "<<") + } + } +} diff --git a/cmd/server.go b/cmd/server.go new file mode 100644 index 0000000..f93127f --- /dev/null +++ b/cmd/server.go @@ -0,0 +1,18 @@ +package main + +import ( + "fmt" + "os" + + "github.com/zorchenhimer/hacker-quotes" +) + +func main() { + server, err := hacker.New("settings.json") + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + server.Hack() +} diff --git a/database/database.go b/database/database.go new file mode 100644 index 0000000..b1a4168 --- /dev/null +++ b/database/database.go @@ -0,0 +1,76 @@ +package database + +import ( + "fmt" + "os" + + "github.com/zorchenhimer/hacker-quotes/models" +) + +type DbType string +const ( + DB_Json DbType = "json" + DB_PostgresSQL DbType = "pgsql" + DB_SQLite DbType = "sqlite" +) + +type DB interface { + // Sentence returns a format string for a sentence with the given ID. + //Sentence(id int) (string, error) + + AddAdjective(word models.Adjective) error + AddNoun(word models.Noun) error + AddVerb(word models.Verb) error + + RemoveAdjective(id int) error + RemoveNoun(id int) error + RemoveVerb(id int) error + + GetAdjectiveIds() ([]int, error) + GetNounIds() ([]int, error) + GetVerbIds() ([]int, error) + + GetAdjective(id int) (*models.Adjective, error) + GetNoun(id int) (*models.Noun, error) + GetVerb(id int) (*models.Verb, error) + + InitData([]models.Adjective, []models.Noun, []models.Verb, []string) error + IsNew() bool + Close() +} + +type dbInit func(connectionString string) (DB, error) +var registered map[DbType]dbInit + +func New(databaseType DbType, connectionString string) (DB, error) { + f, ok := registered[databaseType] + if !ok { + return nil, fmt.Errorf("Unregistered database type: %s", databaseType) + } + + return f(connectionString) +} + +func register(databaseType DbType, initFunc dbInit) { + if registered == nil { + registered = make(map[DbType]dbInit) + } + if _, exists := registered[databaseType]; exists { + panic(fmt.Sprintf("Unable to register database with type %s: already exists.", databaseType)) + } + + registered[databaseType] = initFunc +} + +// fileExists returns whether the given file or directory exists or not. +// Taken from https://stackoverflow.com/a/10510783 +func fileExists(path string) bool { + _, err := os.Stat(path) + if err == nil { + return true + } + if os.IsNotExist(err) { + return false + } + return true +} diff --git a/database/sqlite.go b/database/sqlite.go new file mode 100644 index 0000000..67bb4f6 --- /dev/null +++ b/database/sqlite.go @@ -0,0 +1,325 @@ +package database + +import ( + "database/sql" + "fmt" + + _ "github.com/mattn/go-sqlite3" + + "github.com/zorchenhimer/hacker-quotes/models" +) + +func init() { + register(DB_SQLite, sqliteInit) +} + +type sqliteDb struct { + db *sql.DB + isNew bool +} + +func sqliteInit(connectionString string) (DB, error) { + fmt.Println("[sqlite] DB file:", connectionString) + + newDb := false + if !fileExists(connectionString) { + newDb = true + } + + db, err := sql.Open("sqlite3", fmt.Sprintf("file:%s", connectionString)) + if err != nil { + fmt.Println("[sqlite] Open error:", err) + return nil, err + } + + if newDb { + stmt := ` + create table Adjectives (id integer not null primary key, absolute bool, appendMore bool, appendEst bool, word text); + create table Nouns (id integer not null primary key, multiple bool, begin bool, end bool, alone bool, regular bool, word text); + create table Verbs (id integer not null primary key, regular bool, word text); + ` + //create table Sentences (id integer not null primary key, sentence text) + + if _, err := db.Exec(stmt); err != nil { + fmt.Println("[sqlite], DB table creation error:", err) + return nil, err + } + } + + fmt.Println("[sqlite] no errors") + return &sqliteDb{db: db, isNew: newDb}, nil +} + +func (s *sqliteDb) prep(query string) (*sql.Tx, *sql.Stmt, error) { + tx, err := s.db.Begin() + if err != nil { + return nil, nil, err + } + + stmt, err := tx.Prepare(query) + if err != nil { + tx.Rollback() + return nil, nil, err + } + + return tx, stmt, nil +} + +func (s *sqliteDb) Sentence(id int) (string, error) { + stmt, err := s.db.Prepare("select from sentences where id = ?") + if err != nil { + return "", err + } + defer stmt.Close() + + var sentence string + if err = stmt.QueryRow(id).Scan(&sentence); err != nil { + return "", err + } + + return sentence, nil +} + +func (s *sqliteDb) AddAdjective(word models.Adjective) error { + tx, stmt, err := s.prep("insert into Adjectives (Absolute, AppendMore, AppendEst, Word) values (?, ?, ?, ?)") + if err != nil { + return err + } + defer stmt.Close() + + if _, err := stmt.Exec(word.Absolute, word.AppendMore, word.AppendEst, word.Word); err != nil { + txerr := tx.Rollback() + if txerr != nil { + return fmt.Errorf("rollback error: %v; exec error: %v", txerr, err) + } + return err + } + + return tx.Commit() +} + +func (s *sqliteDb) AddNoun(word models.Noun) error { + tx, stmt, err := s.prep("insert into nouns (Multiple, Begin, End, Alone, Regular, Word) values (?, ?, ?, ?, ?, ?)") + if err != nil { + return err + } + defer stmt.Close() + + if _, err := stmt.Exec(word.Multiple, word.Begin, word.End, word.Alone, word.Regular, word.Word); err != nil { + txerr := tx.Rollback() + if txerr != nil { + return fmt.Errorf("rollback error: %v; exec error: %v", txerr, err) + } + return err + } + + return tx.Commit() +} + +func (s *sqliteDb) AddVerb(word models.Verb) error { + tx, stmt, err := s.prep("insert into verbs (Regular, Word) values (?, ?)") + if err != nil { + return err + } + defer stmt.Close() + + if _, err := stmt.Exec(word.Regular, word.Word); err != nil { + txerr := tx.Rollback() + if txerr != nil { + return fmt.Errorf("rollback error: %v; exec error: %v", txerr, err) + } + return err + } + + return tx.Commit() +} + +func (s *sqliteDb) removeWord(query string, id int) error { + stmt, err := s.db.Prepare(query) + if err != nil { + return err + } + defer stmt.Close() + + _, err = stmt.Exec(id) + return err +} + +func (s *sqliteDb) RemoveAdjective(id int) error { + return s.removeWord("delete from adjectives where id = ?", id) +} + +func (s *sqliteDb) RemoveNoun(id int) error { + return s.removeWord("delete from nouns where id = ?", id) +} + +func (s *sqliteDb) RemoveVerb(id int) error { + return s.removeWord("delete from verbs where id = ?", id) +} + +func (s *sqliteDb) readIds(query string) ([]int, error) { + rows, err := s.db.Query("select id from adjectives") + if err != nil { + return nil, err + } + defer rows.Close() + + ids := []int{} + for rows.Next() { + var id int + if err = rows.Scan(&id); err != nil { + return nil, err + } + ids = append(ids, id) + } + + return ids, rows.Err() +} + +func (s *sqliteDb) GetAdjectiveIds() ([]int, error) { + return s.readIds("select id from adjectives") +} + +func (s *sqliteDb) GetNounIds() ([]int, error) { + return s.readIds("select id from nouns") +} + +func (s *sqliteDb) GetVerbIds() ([]int, error) { + return s.readIds("select id from verbs") +} + +func (s *sqliteDb) GetAdjective(id int) (*models.Adjective, error) { + stmt, err := s.db.Prepare("select Id, Absolute, AppendMore, AppendEst, Word from Adjectives where id = ?") + if err != nil { + return nil, err + } + defer stmt.Close() + + adj := &models.Adjective{} + if err = stmt.QueryRow(id).Scan(&adj.Id, &adj.Absolute, &adj.AppendMore, &adj.AppendEst, &adj.Word); err != nil { + return nil, err + } + + return adj, nil +} + +func (s *sqliteDb) GetNoun(id int) (*models.Noun, error) { + stmt, err := s.db.Prepare("select Id, Multiple, Begin, End, Alone, Regular, Word from Nouns where id = ?") + if err != nil { + return nil, err + } + defer stmt.Close() + + noun := &models.Noun{} + if err = stmt.QueryRow(id).Scan(&noun.Id, &noun.Multiple, &noun.Begin, &noun.End, &noun.Alone, &noun.Regular, &noun.Word); err != nil { + return nil, err + } + + return noun, nil +} + +func (s *sqliteDb) GetVerb(id int) (*models.Verb, error) { + stmt, err := s.db.Prepare("select Id, Regular, Word from Verbs where id = ?") + if err != nil { + return nil, err + } + defer stmt.Close() + + verb := &models.Verb{} + if err = stmt.QueryRow(id).Scan(&verb.Id, &verb.Regular, &verb.Word); err != nil { + return nil, err + } + + return verb, nil +} + +func (s *sqliteDb) InitData(adjectives []models.Adjective, nouns []models.Noun, verbs []models.Verb, sentences []string) error { + tx, err := s.db.Begin() + if err != nil { + return err + } + + astmt_txt := "insert into adjectives (Absolute, AppendMore, AppendEst, Word) values (?, ?, ?, ?)" + fmt.Println(astmt_txt) + + astmt, err := tx.Prepare(astmt_txt) + if err != nil { + tx.Rollback() + return err + } + + for _, adj := range adjectives { + _, err = astmt.Exec(adj.Absolute, adj.AppendMore, adj.AppendEst, adj.Word) + if err != nil { + tx.Rollback() + return err + } + } + astmt.Close() + + nstmt_txt := "insert into nouns (Multiple, Begin, End, Alone, Regular, Word) values (?, ?, ?, ?, ?, ?)" + fmt.Println(nstmt_txt) + + nstmt, err := tx.Prepare(nstmt_txt) + if err != nil { + tx.Rollback() + return err + } + + for _, noun := range nouns { + if _, err = nstmt.Exec(noun.Multiple, noun.Begin, noun.End, noun.Alone, noun.Regular, noun.Word); err != nil { + tx.Rollback() + return err + } + } + nstmt.Close() + + vstmt_txt := "insert into verbs (Regular, Word) values (?, ?)" + fmt.Println(vstmt_txt) + + vstmt, err := tx.Prepare(vstmt_txt) + if err != nil { + tx.Rollback() + return err + } + + for _, verb := range verbs { + _, err = vstmt.Exec(verb.Regular, verb.Word) + if err != nil { + tx.Rollback() + return err + } + } + vstmt.Close() + + //sstmt, err := tx.Prepare("insert into sentences (Sentence) values (?)") + //if err != nil { + // tx.Rollback() + // return err + //} + + //for _, sentence := range sentences { + // _, err = sstmt.Exec(sentence) + // if err != nil { + // tx.Rollback() + // return err + // } + //} + //sstmt.Close() + + err = tx.Commit() + if err != nil { + return err + } + + s.isNew = false + return nil +} + +func (s *sqliteDb) IsNew() bool { + return s.isNew +} + +func (s *sqliteDb) Close() { + s.db.Close() +} diff --git a/frontend/frontend.go b/frontend/frontend.go new file mode 100644 index 0000000..3982659 --- /dev/null +++ b/frontend/frontend.go @@ -0,0 +1,45 @@ +package frontend + +import ( + "net/http" + "html/template" + + //"github.com/gorilla/sessions" + + //"github.com/zorchenhimer/hacker-quotes/models" + //"github.com/zorchenhimer/hacker-quotes/database" + "github.com/zorchenhimer/hacker-quotes/business" +) + +type Frontend struct { + //db database.DB + bs business.HackerQuotes + //cookies *sessions.CookieStore + templates map[string]*template.Template +} + +func New(bs business.HackerQuotes) (*Frontend, error) { + f := &Frontend{ + bs: bs, + //cookies: sessions.NewCookieStore([]byte("some auth key"), []byte("some encrypt key")), + } + return f, nil +} + +func (f *Frontend) ServeHTTP(w http.ResponseWriter, r *http.Request) { + //parts := strings.Split(r.URL.Path, "/") + switch r.URL.Path { + case "/": + f.home(w, r) + + //case "/admin": + // f.admin(w, r) + + default: + f.notFound(w, r) + } +} + +func (f *Frontend) notFound(w http.ResponseWriter, r *http.Request) { + http.NotFound(w, r) +} diff --git a/frontend/home.go b/frontend/home.go new file mode 100644 index 0000000..325c55e --- /dev/null +++ b/frontend/home.go @@ -0,0 +1,14 @@ +package frontend + +import ( + "net/http" +) + +func (f *Frontend) home(w http.ResponseWriter, r *http.Request) { + words, err := f.bs.Random() + if err != nil { + w.Write([]byte(err.Error())) + return + } + w.Write([]byte(words)) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..e4bf34a --- /dev/null +++ b/go.mod @@ -0,0 +1,8 @@ +module github.com/zorchenhimer/hacker-quotes + +go 1.15 + +require ( + github.com/gorilla/sessions v1.2.1 + github.com/mattn/go-sqlite3 v1.14.6 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..4f00ed8 --- /dev/null +++ b/go.sum @@ -0,0 +1,6 @@ +github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= +github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= +github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= +github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= +github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg= +github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= diff --git a/models/adjective.go b/models/adjective.go new file mode 100644 index 0000000..fcd2c32 --- /dev/null +++ b/models/adjective.go @@ -0,0 +1,11 @@ +package models + +type Adjective struct { + Id int + + Absolute bool + AppendMore bool + AppendEst bool + + Word string +} diff --git a/models/noun.go b/models/noun.go new file mode 100644 index 0000000..a34c3c6 --- /dev/null +++ b/models/noun.go @@ -0,0 +1,42 @@ +package models + +import ( + "strings" +) + +type Noun struct { + Id int + + Multiple bool + + Begin bool + End bool + Alone bool + + Regular bool + + Word string +} + +func (n Noun) Plural() string { + suffixes := []string{ + "s", + "x", + "sh", + "ch", + "ss", + } + + for _, sfx := range suffixes { + if strings.HasSuffix(n.Word, sfx) { + return n.Word + "es" + } + } + + if strings.HasSuffix(n.Word, "y") && !strings.ContainsAny(string(n.Word[len(n.Word)-2]), "aeiou") { + return n.Word[:len(n.Word)-1] + "ies" + } + + return n.Word + "s" +} + diff --git a/models/verb.go b/models/verb.go new file mode 100644 index 0000000..15634d6 --- /dev/null +++ b/models/verb.go @@ -0,0 +1,28 @@ +package models + +type Verb struct { + Id int + + Regular bool + + // Indefinite form + Word string + + ConjugationsPast []Conjugate + ConjugationsPresent []Conjugate + ConjugationsFuture []Conjugate +} + +type ConjugateType int +const ( + CT_I ConjugateType = iota + CT_You + CT_It + CT_We + CT_They +) + +type Conjugate struct { + Type ConjugateType + Form string +} diff --git a/server.go b/server.go new file mode 100644 index 0000000..948058d --- /dev/null +++ b/server.go @@ -0,0 +1,87 @@ +package hacker + +import ( + "net/http" + "fmt" + + //"github.com/zorchenhimer/hacker-quotes/api" + "github.com/zorchenhimer/hacker-quotes/business" + "github.com/zorchenhimer/hacker-quotes/database" + "github.com/zorchenhimer/hacker-quotes/frontend" + //"github.com/zorchenhimer/hacker-quotes/models" +) + +type Server struct { + db database.DB + hs *http.Server + bs business.HackerQuotes + + settings *settings +} + +// New returns a new Server object with the settings from configFile. +// If no file is specified, a default "settings.config" will be +// created with default settings in the current working directory. +func New(configFile string) (*Server, error) { + s := &Server{} + + if settings, err := loadSettings(configFile); err != nil { + return nil, fmt.Errorf("Unable to load settings: %s", err) + } else { + s.settings = settings + } + + db, err := database.New(s.settings.DatabaseType, s.settings.ConnectionString) + if err != nil { + return nil, fmt.Errorf("Unable to load database type %s: %s", s.settings.DatabaseType, err) + } + + s.db = db + + bs, err := business.NewGeneric(db) + if err != nil { + return nil, err + } + s.bs = bs + + if s.db.IsNew() { + fmt.Println("database is new") + err = bs.InitData("word_lists.json") + if err != nil { + return nil, err + } + } else { + fmt.Println("database isn't new") + } + + + web, err := frontend.New(s.bs) + if err != nil { + return nil, fmt.Errorf("Unable to load frontend: %s", err) + } + + mux := http.NewServeMux() + //mux.Handle("/api", api) + mux.Handle("/", web) + + hs := &http.Server{ + Addr: ":8080", + Handler: mux, + } + + s.hs = hs + + return s, nil +} + +func (s *Server) Hack() error { + if err := s.hs.ListenAndServe(); err != nil && err != http.ErrServerClosed { + fmt.Println("Error running HTTP server:", err) + } + + return nil +} + +func (s *Server) Shutdown() error { + return fmt.Errorf("Not implemented") +} diff --git a/settings.go b/settings.go new file mode 100644 index 0000000..2038396 --- /dev/null +++ b/settings.go @@ -0,0 +1,51 @@ +package hacker + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + + "github.com/zorchenhimer/hacker-quotes/database" +) + +type settings struct { + DatabaseType database.DbType + ConnectionString string + + HttpAddr string +} + +func loadSettings(filename string) (*settings, error) { + if !fileExists(filename) { + return nil, fmt.Errorf("%q doesn't exist", filename) + //return &settings{ + // HttpAddr: ":8080", + //}, nil + } + + raw, err := ioutil.ReadFile(filename) + if err != nil { + return nil, fmt.Errorf("Error reading file: %s", err) + } + + s := &settings{} + if err = json.Unmarshal(raw, s); err != nil { + return nil, fmt.Errorf("Error unmarshaling: %s", err) + } + + return s, nil +} + +// fileExists returns whether the given file or directory exists or not. +// Taken from https://stackoverflow.com/a/10510783 +func fileExists(path string) bool { + _, err := os.Stat(path) + if err == nil { + return true + } + if os.IsNotExist(err) { + return false + } + return true +} diff --git a/word_lists.json b/word_lists.json new file mode 100644 index 0000000..88324e0 --- /dev/null +++ b/word_lists.json @@ -0,0 +1,405 @@ +{ + "adjectives": [ + ["tm", "quantum"], + ["tm", "virtual"], + ["ta", "shortened"], + ["tm", "common"], + ["te", "raw"], + ["tm", "random"], + ["ta", "audio"], + ["tm", "industrial"], + ["tm", "3D-printed"], + ["tm", "proprietary"], + ["tm", "forbidden"], + ["ta", "patented"], + ["tm", "neural"], + ["tm", "wireless"], + ["tm", "online"], + ["tm", "offline"], + ["tm", "primary"], + ["ta", "8-bit"], + ["tm", "open"], + ["ta", "live"], + ["tm", "flexible"], + ["tm", "adaptive"], + ["tm", "artificial"], + ["tm", "mobile"], + ["tm", "meta"], + ["tm", "regular"], + ["tm", "variable"], + ["tm", "global"], + ["tm", "automatic"], + ["tm", "geometric"], + ["tm", "inductive"], + ["tm", "default"], + ["tm", "constant"], + ["tm", "asymmetric"], + ["tm", "symmetric"], + ["tm", "robust"], + ["tm", "analog"], + ["tm", "digital"], + ["tm", "micro"], + ["tm", "standardized"], + ["tm", "hidden"], + ["tm", "biometric"], + ["tm", "safety"], + ["tm", "emulated"], + ["ta", "root"], + ["ta", "generic"], + ["ta", "bricked"], + ["ta", "miscellaneous"], + ["ta", "haptic"], + ["tm", "logical"], + ["ta", "physical"], + ["ta", "relative"], + ["ta", "lasered"], + ["ta", "licensed"], + ["ta", "robotic"], + ["tm", "immersive"], + ["ta", "customized"], + ["ta", "malfunctioning"], + ["tm", "granular"], + ["ta", "quality"], + ["ta", "open source"], + ["tm", "sophisticated"], + ["tm", "statistical"], + ["tm", "DIY"], + ["ta", "remote"], + ["ta", "handheld"], + ["tm", "bleeding edge"], + ["tm", "modern"], + ["tm", "outdated"], + ["ta", "legacy"], + ["ta", "visual"], + ["ta", "guidance"], + ["tm", "private"], + ["tm", "public"], + ["tm", "cyber"], + ["ta", "numeric"], + ["ta", "mathematical"], + ["ta", "linguistic"], + ["ta", "platform-specific"], + ["ta", "normalized"], + ["ta", "bitwise"], + ["ta", "hexadecimal"], + ["ta", "octal"], + ["ta", "binary"], + ["ta", "decimal"], + ["te", "large"], + ["te", "small"], + ["tm", "normal"], + ["ta", "infected"], + ["ta", "internal"], + ["tm", "synchron"], + ["tm", "asynchron"], + ["ta", "biometric"], + ["ta", "idle"], + ["ta", "external"], + ["ta", "reserved"], + ["ta", "explicit"], + ["ta", "implicit"], + ["tm", "decentralized"], + ["tm", "interactive"], + ["ta", "lambda"], + ["tm", "recursive"], + ["te", "weak"], + ["ta", "matching"], + ["tm", "similar"], + ["te", "strong"], + ["ta", "partial"], + ["ta", "performance"], + ["ta", "build in"] + ], + + "nouns":[ + ["sar", "git"], + ["sar", "github"], + ["sar", "gitlab"], + ["sar", "gitea"], + ["sar", "bitbucket"], + ["smer", "branch"], + ["smer", "commit"], + ["smber", "log"], + ["smar", "pull request"], + ["smar", "merge request"], + ["smber", "stash"], + ["sber", "status"], + ["smber", "tag"], + ["smber", "origin"], + ["smber", "master"], + ["smber", "lemur"], + ["sber", "spacehuhn"], + ["smber", "laser"], + ["smber", "signal"], + ["smber", "network"], + ["smber", "analyzer"], + ["smber", "application"], + ["smber", "firewall"], + ["smber", "cybernuke"], + ["sber", "IRC"], + ["smber", "mainframe"], + ["smber", "server"], + ["smber", "cloud"], + ["smbr", "reality"], + ["smer", "request"], + ["sber", "WiFi"], + ["sber", "Bluetooth"], + ["smber", "cable"], + ["sber", "ethernet"], + ["sber", "LAN"], + ["sber", "WAN"], + ["smber", "antenna"], + ["sber", "NAS"], + ["smar", "power supply"], + ["smber", "grid"], + ["smber", "display"], + ["smber", "monitor"], + ["smber", "microcontroller"], + ["smber", "controller"], + ["ser", "SoC"], + ["sbr", "SBC"], + ["sbr", "ATX"], + ["sbr", "ITX"], + ["sbr", "USB"], + ["ser", "HDD"], + ["ser", "SSD"], + ["smber", "keyboard"], + ["smer", "transition"], + ["smber", "tree"], + ["ser", "SD"], + ["ser", "LED"], + ["ser", "IDE"], + ["smer", "editor"], + ["smer", "frame"], + ["ser", "PoC"], + ["smber", "bucket"], + ["sber", "VM"], + ["smer", "identifier"], + ["sber", "middleware"], + ["sber", "bottleneck"], + ["ser", "UI"], + ["ser", "GUI"], + ["smber", "observer"], + ["smber", "singleton"], + ["smber", "array"], + ["smber", "transmitter"], + ["smber", "DVD"], + ["ber", "logic"], + ["smber", "emulation"], + ["smer", "reader"], + ["smer", "writer"], + ["smer", "label"], + ["smber", "clock"], + ["smber", "MCU"], + ["smber", "phone"], + ["smber", "space"], + ["sber", "data"], + ["sber", "analysis"], + ["smber", "sample"], + ["sber", "intelligence"], + ["smber", "sensor"], + ["smber", "camera"], + ["smber", "battery"], + ["smber", "process"], + ["smber", "website"], + ["smber", "homepage"], + ["smber", "app"], + ["smber", "error"], + ["smber", "warning"], + ["smber", "sequence"], + ["smber", "information"], + ["sbr", "ASCII"], + ["smber", "pattern"], + ["smber", "simulation"], + ["smber", "simulator"], + ["sber", "indicator"], + ["smber", "troll"], + ["smber", "regulator"], + ["smber", "container"], + ["smber", "breadboard"], + ["sber", "IC"], + ["smber", "controller"], + ["smber", "drone"], + ["smber", "deauther"], + ["smar", "if loop"], + ["sar", "GTFO"], + ["sber", "fax"], + ["smar", "garbage collector"], + ["smer", "collector"], + ["smber", "thread"], + ["smber", "model"], + ["smber", "switch"], + ["smber", "dimension"], + ["sber", "foo"], + ["sber", "bar"], + ["smber", "key"], + ["smber", "java"], + ["smber", "coffee"], + ["sbr", "null"], + ["sbr", "NaN"], + ["sbr", "undefined"], + ["smber", "integer"], + ["smber", "double"], + ["smber", "string"], + ["sar", "bare metal"], + ["smber", "adapter"], + ["smber", "framework"], + ["smber", "system"], + ["smber", "algorithm"], + ["sbr", "spacetime"], + ["smbr", "LCD"], + ["sber", "bandwidth"], + ["smber", "virus"], + ["sbr", "UTF-8"], + ["sber", "web"], + ["sbr", "handler"], + ["smber", "exeption"], + ["smber", "path"], + ["smber", "reference"], + ["smber", "template"], + ["smber", "wildcard"], + ["smber", "interface"], + ["sber", "syntax"], + ["smber", "loop"], + ["smber", "demon"], + ["smber", "core"], + ["sber", "interpreter"], + ["smber", "string"], + ["smber", "document"], + ["smber", "cookie"], + ["smber", "codec"], + ["smber", "e-mail"], + ["sber", "OS"], + ["smber", "service"], + ["sber", "provider"], + ["smber", "cache"], + ["smber", "database"], + ["smber", "object"], + ["smbers", "dictionary"], + ["sber", "driver"], + ["smber", "index"], + ["sber", "encoder"], + ["smber", "list"], + ["smber", "tuple"], + ["smber", "range"], + ["smber", "stream"], + ["sber", "internet"], + ["smber", "component"], + ["smber", "module"], + ["smber", "library"], + ["smber", "limit"], + ["smber", "function"], + ["smer", "token"], + ["smber", "code"], + ["smber", "wave"], + ["sber", "IoT"], + ["smber", "blockchain"], + ["smber", "repository"], + ["smber", "northbridge"], + ["smber", "southbridge"] + ], + + "verbs": [ + ["r", "init"], + ["r", "pull"], + ["r", "push"], + ["r", "clone"], + ["r", "fetch"], + ["r", "commit"], + ["r", "rebase"], + ["r", "merge"], + ["r", "fork"], + ["r", "stash"], + ["r", "tag"], + ["r", "cherry-pick"], + ["r", "checkout"], + ["r", "wrap"], + ["r", "iterate"], + ["r", "loop"], + ["r", "port"], + ["r", "infect"], + ["r", "append"], + ["r", "change"], + ["r", "normalize"], + ["r", "average"], + ["r", "decrypt"], + ["r", "encrypt"], + ["r", "convert"], + ["r", "access"], + ["r", "coordinate"], + ["r", "power"], + ["r", "monitor"], + ["r", "regulate"], + ["r", "detect"], + ["r", "block"], + ["r", "debug"], + ["r", "observe"], + ["r", "bypass"], + ["r", "input"], + ["r", "synthesize"], + ["r", "connect"], + ["r", "parse"], + ["r", "generate"], + ["r", "copy"], + ["r", "analyze"], + ["r", "control"], + ["r", "benchmark"], + ["r", "request"], + ["r", "enter"], + ["r", "call"], + ["r", "detect"], + ["r", "print"], + ["r", "simulate"], + ["r", "emulate"], + ["r", "intercept"], + ["r", "sequence"], + ["r", "synchronize"], + ["r", "test"], + ["r", "tokenize"], + ["r", "format"], + ["r", "constrain"], + ["r", "import"], + ["r", "export"], + ["r", "develop"], + ["r", "invert"], + ["r", "slice"], + ["r", "index"], + ["r", "join"], + ["r", "shorten"], + ["r", "partition"], + ["r", "decompile"], + ["r", "split"], + ["r", "swap"], + ["r", "cast"], + ["r", "clear"], + ["r", "stream"], + ["r", "remove"], + ["r", "add"], + ["r", "signal"], + ["r", "process"], + ["r", "disconnect"], + ["r", "start"], + ["r", "interpret"], + ["r", "yield"], + ["r", "match"], + ["r", "inspect"], + ["r", "collect"], + ["r", "restrict"], + ["r", "display"], + ["r", "address"], + ["r", "identify"], + ["r", "load"], + ["r", "define"], + ["r", "decentralize"], + ["r", "update"], + ["r", "encode"], + ["r", "virtualize"], + ["r", "transmit"], + ["r", "compile"], + ["r", "randomize"], + ["r", "deauth"], + ["r", "alter"], + ["r", "mute"], + ["r", "hash"] + ] +}