Compare commits

..

No commits in common. "cb83223ab80b91f8282e630fd13d7dac0c3a63b5" and "e7ff5606f18f91a8dcfbdcfb0d40cab30fb26ffd" have entirely different histories.

6 changed files with 43 additions and 229 deletions

View File

@ -1,7 +1,6 @@
package main package main
import ( import (
"bytes"
"fmt" "fmt"
"log" "log"
"os" "os"
@ -9,25 +8,29 @@ import (
"strings" "strings"
"time" "time"
"git.tilde.town/tildetown/town/signup" "encoding/json"
"github.com/MakeNowJust/heredoc/v2" "github.com/MakeNowJust/heredoc/v2"
"github.com/gdamore/tcell/v2" "github.com/gdamore/tcell/v2"
"github.com/rivo/tview" "github.com/rivo/tview"
) )
// TODO remove sidebar
// TODO add /help
const ( const (
maxInputLength = 10000 maxInputLength = 10000
logDir = "/town/var/signups/log" signupDirectory = "/town/signups"
logDir = "/town/var/signup"
) )
type scene struct { type scene struct {
Name string Name string
Description string Description string
Key string
Host *character Host *character
Write func([]byte) }
type townApplication struct {
When time.Time
Answers map[string]string
} }
type character struct { type character struct {
@ -62,21 +65,14 @@ func main() {
} }
logger := log.New(logF, "", log.Ldate|log.Ltime) logger := log.New(logF, "", log.Ldate|log.Ltime)
err = _main(logger)
db, err := signup.NewDB()
if err != nil { if err != nil {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
os.Exit(2) os.Exit(2)
} }
err = _main(logger, db)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(3)
}
} }
func _main(l *log.Logger, db *signup.DB) error { func _main(l *log.Logger) error {
l.Println("starting a session") l.Println("starting a session")
pages := tview.NewPages() pages := tview.NewPages()
mainFlex := tview.NewFlex() mainFlex := tview.NewFlex()
@ -130,15 +126,31 @@ func _main(l *log.Logger, db *signup.DB) error {
player := newCharacter("you", "TODO") player := newCharacter("you", "TODO")
su := &signup.TownSignup{ID: -1} townApp := &townApplication{
Answers: map[string]string{},
}
save := func() { save := func() {
su.Created = time.Now() townApp.When = time.Now()
err := db.InsertSignup(su) output, err := json.Marshal(townApp)
if err != nil { if err != nil {
l.Printf("failed to write to db: %s", err.Error()) l.Printf("could not serialize stuff: %s", err.Error())
l.Printf("dumping values: %#v", su) l.Printf("dumping values: %v", townApp)
return
}
f, err := os.Create(path.Join(signupDirectory, fmt.Sprintf("%d.json", townApp.When.Unix())))
if err != nil {
l.Printf("could not open signup file: %s", err.Error())
l.Printf("dumping values: %s", string(output))
return
}
defer f.Close()
_, err = f.Write(output)
if err != nil {
l.Printf("failed to write to file: %s", err.Error())
l.Printf("dumping values: %s", string(output))
return return
} }
} }
@ -164,9 +176,7 @@ func _main(l *log.Logger, db *signup.DB) error {
when you're ready to move on, [-:-:b]/nod[-:-:-] when you're ready to move on, [-:-:b]/nod[-:-:-]
`), `),
Host: newCharacter("wire guy", "a lil homonculus made of discarded computer cables"), Host: newCharacter("wire guy", "a lil homonculus made of discarded computer cables"),
Write: func(b []byte) { Key: "email",
su.Email = string(b)
},
}, },
{ {
Name: "nodded", Name: "nodded",
@ -188,9 +198,7 @@ func _main(l *log.Logger, db *signup.DB) error {
just say your answer out loud. when you've said what you want, [-:-:b]/lean[-:-:-] just say your answer out loud. when you've said what you want, [-:-:b]/lean[-:-:-]
against a tree. against a tree.
`), `),
Write: func(b []byte) { Key: "how",
su.How = string(b)
},
Host: newCharacter("the shrike", "a little grey bird. it has a pretty song."), Host: newCharacter("the shrike", "a little grey bird. it has a pretty song."),
}, },
{ {
@ -209,9 +217,7 @@ func _main(l *log.Logger, db *signup.DB) error {
as usual, just say your answer. when you're satisfied, please [-:-:b]/spin[-:-:-] as usual, just say your answer. when you're satisfied, please [-:-:b]/spin[-:-:-]
around in this void. around in this void.
`), `),
Write: func(b []byte) { Key: "why",
su.Why = string(b)
},
Host: newCharacter("the vcr", "a black and grey VCR from 1991"), Host: newCharacter("the vcr", "a black and grey VCR from 1991"),
}, },
{ {
@ -235,9 +241,7 @@ func _main(l *log.Logger, db *signup.DB) error {
when you're happy you can submit this whole experience by leaving the when you're happy you can submit this whole experience by leaving the
store. just [-:-:b]/open[-:-:-] the door. store. just [-:-:b]/open[-:-:-] the door.
`), `),
Write: func(b []byte) { Key: "where",
su.Links = string(b)
},
Host: newCharacter("the mop", "a greying mop with a wooden handle."), Host: newCharacter("the mop", "a greying mop with a wooden handle."),
}, },
{ {
@ -251,29 +255,21 @@ func _main(l *log.Logger, db *signup.DB) error {
ok bye have a good one~ ok bye have a good one~
`), `),
Write: func(b []byte) { Key: "extra",
su.Extra = string(b)
},
}, },
} }
sceneIx := 0 sceneIx := 0
currentScene := scenes[sceneIx] currentScene := scenes[sceneIx]
inputWriter := bytes.NewBuffer([]byte{})
advanceScene := func(fromScene, sorryMsg string) { advanceScene := func(fromScene, sorryMsg string) {
if currentScene.Name != fromScene { if currentScene.Name != fromScene {
return return
} }
if inputWriter.Len() == 0 { if len(townApp.Answers[currentScene.Key]) == 0 {
fmt.Fprintln(msgScroll, currentScene.Host.Say(sorryMsg)) fmt.Fprintln(msgScroll, currentScene.Host.Say(sorryMsg))
return return
} }
l.Println("advancing scene")
currentScene.Write(inputWriter.Bytes())
inputWriter = bytes.NewBuffer([]byte{})
sceneIx++ sceneIx++
currentScene = scenes[sceneIx] currentScene = scenes[sceneIx]
fmt.Fprintln(msgScroll, heredoc.Doc(` fmt.Fprintln(msgScroll, heredoc.Doc(`
@ -314,23 +310,15 @@ func _main(l *log.Logger, db *signup.DB) error {
} }
return return
} }
if inputWriter.Len() > maxInputLength { if len(townApp.Answers[currentScene.Key]) > maxInputLength {
fmt.Fprintln(msgScroll, fmt.Fprintln(msgScroll,
currentScene.Host.Say("sorry I've heard more than I can remember :( maybe it's time to move on")) currentScene.Host.Say("sorry I've heard more than I can remember :( maybe it's time to move on"))
return return
} }
fmt.Fprintln(msgScroll, player.Say(msg)) fmt.Fprintln(msgScroll, player.Say(msg))
fmt.Fprintln(inputWriter, msg) townApp.Answers[currentScene.Key] += ("\n" + msg)
} }
defer func() {
if currentScene.Name == "done" {
currentScene.Write(inputWriter.Bytes())
db.UpdateSignup(su)
}
db.Close()
}()
app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
switch event.Key() { switch event.Key() {
case tcell.KeyCtrlD: case tcell.KeyCtrlD:

1
go.mod
View File

@ -24,7 +24,6 @@ require (
github.com/mattn/go-colorable v0.1.2 // indirect github.com/mattn/go-colorable v0.1.2 // indirect
github.com/mattn/go-isatty v0.0.13 // indirect github.com/mattn/go-isatty v0.0.13 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/mattn/go-sqlite3 v1.14.16 // indirect
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
github.com/microcosm-cc/bluemonday v1.0.17 // indirect github.com/microcosm-cc/bluemonday v1.0.17 // indirect
github.com/muesli/reflow v0.3.0 // indirect github.com/muesli/reflow v0.3.0 // indirect

2
go.sum
View File

@ -41,8 +41,6 @@ github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/microcosm-cc/bluemonday v1.0.17 h1:Z1a//hgsQ4yjC+8zEkV8IWySkXnsxmdSY642CTFQb5Y= github.com/microcosm-cc/bluemonday v1.0.17 h1:Z1a//hgsQ4yjC+8zEkV8IWySkXnsxmdSY642CTFQb5Y=

View File

@ -1,116 +0,0 @@
package signup
import (
"database/sql"
"time"
_ "github.com/mattn/go-sqlite3"
)
type AdminNote struct {
ID int64
Admin string
Note string
When time.Time
}
type SignupDecision string
type UserState string
const (
SignupAccepted = "accepted"
SignupRejected = "rejected"
StateActive = "active"
StateTempBan = "temp_banned"
StateBan = "banned"
)
type TownSignup struct {
ID int64
Created time.Time
Email string
How string
Why string
Links string
Extra string
Notes []AdminNote
Decision SignupDecision
}
type TownAccount struct {
ID int
Emails []string
Username string
Signup int
Notes []AdminNote
State UserState
Admin bool
}
type DB struct {
db *sql.DB
}
func NewDB() (*DB, error) {
db, err := sql.Open("sqlite3", "/town/var/signups/signups.db?mode=rw")
if err != nil {
return nil, err
}
return &DB{
db: db,
}, nil
}
func (d *DB) InsertSignup(su *TownSignup) error {
stmt, err := d.db.Prepare(`
INSERT INTO signups (created, email, how, why, links, extra) VALUES(
?, ?, ?, ?, ?, ?
) RETURNING id
`)
if err != nil {
return err
}
result, err := stmt.Exec(su.Created.Unix(), su.Email, su.How, su.Why, su.Links, su.Extra)
if err != nil {
return err
}
defer stmt.Close()
liid, err := result.LastInsertId()
if err != nil {
return err
}
su.ID = liid
return nil
}
func (d *DB) UpdateSignup(su *TownSignup) error {
if su.ID < 0 {
return nil
}
stmt, err := d.db.Prepare(`
UPDATE signups (email, how, why, links, extra) VALUES(
?, ?, ?, ?, ?
)
`)
if err != nil {
return err
}
_, err = stmt.Exec(su.Email, su.How, su.Why, su.Links, su.Extra)
if err != nil {
return err
}
defer stmt.Close()
return nil
}
func (d *DB) Close() error {
return d.db.Close()
}

View File

@ -1,10 +0,0 @@
CREATE TABLE IF NOT EXISTS signups (
id INTEGER PRIMARY KEY,
created TEXT DEFAULT (strftime('%Y-%m-%dT%H:%M', 'now', 'localtime')),
email TEXT,
how TEXT,
why TEXT,
links TEXT,
extra TEXT,
decision TEXT
);

View File

@ -1,45 +0,0 @@
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
created TEXT DEFAULT (strftime('%Y-%m-%dT%H:%M', 'now', 'localtime')),
username TEXT UNIQUE,
signupid INTEGER,
state TEXT,
admin INTEGER DEFAULT FALSE,
FOREIGN KEY (signupid) REFERENCES signups(signupid)
);
CREATE TABLE IF NOT EXISTS emails (
id INTEGER PRIMARY KEY,
address TEXT UNIQUE,
userid INTEGER,
FOREIGN KEY (userid) REFERENCES users(userid)
);
CREATE TABLE IF NOT EXISTS user_notes (
noteid INTEGER,
userid INTEGER,
PRIMARY KEY (noteid, userid),
FOREIGN KEY (noteid) REFERENCES notes(noteid),
FOREIGN KEY (userid) REFERENCES users(userid)
);
CREATE TABLE IF NOT EXISTS notes (
id INTEGER PRIMARY KEY,
adminid INTEGER,
text TEXT,
created TEXT DEFAULT (strftime('%Y-%m-%dT%H:%M', 'now', 'localtime')),
FOREIGN KEY (adminid) REFERENCES users(adminid)
);
CREATE TABLE IF NOT EXISTS signup_notes (
noteid INTEGER,
signupid INTEGER,
PRIMARY KEY (noteid, signupid),
FOREIGN KEY (noteid) REFERENCES notes(noteid),
FOREIGN KEY (signupid) REFERENCES signups(signupid)
);