Compare commits
No commits in common. "cb83223ab80b91f8282e630fd13d7dac0c3a63b5" and "e7ff5606f18f91a8dcfbdcfb0d40cab30fb26ffd" have entirely different histories.
cb83223ab8
...
e7ff5606f1
|
@ -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
1
go.mod
|
@ -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
2
go.sum
|
@ -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=
|
||||||
|
|
116
signup/signup.go
116
signup/signup.go
|
@ -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()
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
);
|
|
|
@ -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)
|
|
||||||
);
|
|
Loading…
Reference in New Issue