Loading .gitignore +1 −0 Original line number Diff line number Diff line /bulletin /convert-vms-record-fmt *.bz2 *.zip Loading NOTES.md +7 −1 Original line number Diff line number Diff line Loading @@ -29,7 +29,7 @@ repl.commands? * Implement each command. * Next: folder commands - ~~CREATE~~, ~~REMOVE~~, MODIFY, ~~INDEX~~, ~~SELECT~~ * Messages: ADD, CURRENT, DIRECTORY, BACK, CHANGE, FIRST, REMOVE, NEXT, READ * Messages: ~~ADD~~, CURRENT, DIRECTORY, BACK, CHANGE, FIRST, REMOVE, NEXT, READ * Messages edit: CHANGE, REPLY, FORWARD * Moving messages: COPY, MOVE * Compound commands: SET and SHOW Loading @@ -42,12 +42,18 @@ repl.commands? * Using giu, a [text-editor](https://serge-hulne.medium.com/coding-a-simple-text-editor-in-go-using-giu-quick-and-dirty-b9b97ab41e4a) (needs cgo, no) * [bubbletea](https://github.com/charmbracelet/bubbletea) seems to be the tui that's winning * Another option is tview - [simpler](https://github.com/rivo/tview). * Handle broadcast messages - have bulletin watch a directory and display files from it. Then have them delete the file if it's older than 5 minutes (allow for failure) * Cleanup help output. * Remove the node/cluster/newsgroup/mailing-list related flags. * Remove BBOARD references. * format with `par w72j1` * Database * trigger to limit values for 'visibility'? * Add some of the early announcements from the sources - see the conversion branch - to the GENERAL folder. * Add a pager * Add commands: * A way to add / delete ssh keys. * A way to manage files? Loading accounts/accounts.go +12 −3 Original line number Diff line number Diff line Loading @@ -56,12 +56,10 @@ func Open(login string) error { user.Admin = 0 fmt.Printf("Welcome new user %s\n", login) rl, err := readline.New("please enter your name: ") user.Name, err = GetLine("please enter your name: ") if err != nil { return err } user.Name, err = rl.Readline() rl.Close() err = User.Folders.AddUser(*user) if err != nil { Loading @@ -84,3 +82,14 @@ func Open(login string) error { func (u *UserData) Close() { u.Folders.Close() } // GetLine gets a line. func GetLine(prompt string) (string, error) { rl, err := readline.New(prompt) if err != nil { return "", err } defer rl.Close() line, err := rl.Readline() return line, err } folders/messages.go +60 −3 Original line number Diff line number Diff line // Package folders are all the routines and sql for managing folders. package folders import "time" import ( "fmt" "strings" "time" ) // CreateMessage creates a new folder. func (s *Store) CreateMessage(author, subject, message, folder string, permanent, shutdown int, expiration *time.Time) error { if expiration == nil { var days int err := s.db.Get(&days, "SELECT expire FROM folders WHERE name = $1", folder) if err != nil { return err } if days <= 0 { days = 14 } exp := time.Now().AddDate(0, 0, days) expiration = &exp } // TODO: replace _ with rows and check. _, err := s.db.Exec( `INSERT INTO messages (id, folder, author, subject, message, permanent, shutdown, expiration) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`, 1, // TODO: how to set this. ((SELECT COALESCE(MAX(id), 0) + 1 FROM messages WHERE folder = $1), $1, $2, $3, $4, $5, $6, $7)`, folder, author, subject, Loading @@ -22,3 +38,44 @@ func (s *Store) CreateMessage(author, subject, message, folder string, permanent // TODO: process this error a bit more to give a better error message. return err } // Message contains a message type Message struct { ID int `db:"id"` Folder string `db:"folder"` Author string `db:"author"` Subject string `db:"subject"` Message string `db:"message"` Expires time.Time `db:"expiration"` CreateAt time.Time `db:"create_at"` UpdateAt time.Time `db:"update_at"` } // String renders a message. func (m *Message) String() string { buf := &strings.Builder{} // TODO: Show if an edit has happened. fmt.Fprintf(buf, "From: \"%s\" %s\n", m.Author, m.CreateAt.Format("02-JAN-2006 15:04:05")) fmt.Fprintf(buf, "To: %s\n", m.Folder) fmt.Fprintf(buf, "Subj: %s\n\n", m.Subject) fmt.Fprintf(buf, "%s\n", m.Message) return buf.String() } // ReadMessage reads a message for a user. func (s *Store) ReadMessage(login, folder string, msgid int) (*Message, error) { msg := &Message{} err := s.db.Get(msg, `SELECT id, folder, author, subject, message, expiration, create_at, update_at FROM messages WHERE folder = $1, id = $2`, folder, msgid) if err != nil { return nil, err } // TODO: replace _ with rows and check. _, err = s.db.Exec( "INSERT INTO read (login, folder, msgid) VALUES ($1, $2, $3)", login, folder, msgid) return msg, err } folders/sql/1_create_table.up.sql +11 −0 Original line number Diff line number Diff line Loading @@ -117,3 +117,14 @@ CREATE TABLE messages ( CREATE INDEX messages_idx_shutdown ON messages(shutdown); CREATE INDEX messages_idx_expiration ON messages(expiration); CREATE TABLE read ( login VARCHAR(25) REFERENCES users(login) ON DELETE CASCADE ON UPDATE CASCADE, folder VARCHAR(25) REFERENCES folders(name) ON DELETE CASCADE ON UPDATE CASCADE, msgid INT, PRIMARY KEY (folder, login, msgid), CONSTRAINT FK_id_folder FOREIGN KEY (msgid, folder) REFERENCES messages(id, folder) ON DELETE CASCADE ON UPDATE CASCADE ) WITHOUT ROWID; Loading
.gitignore +1 −0 Original line number Diff line number Diff line /bulletin /convert-vms-record-fmt *.bz2 *.zip Loading
NOTES.md +7 −1 Original line number Diff line number Diff line Loading @@ -29,7 +29,7 @@ repl.commands? * Implement each command. * Next: folder commands - ~~CREATE~~, ~~REMOVE~~, MODIFY, ~~INDEX~~, ~~SELECT~~ * Messages: ADD, CURRENT, DIRECTORY, BACK, CHANGE, FIRST, REMOVE, NEXT, READ * Messages: ~~ADD~~, CURRENT, DIRECTORY, BACK, CHANGE, FIRST, REMOVE, NEXT, READ * Messages edit: CHANGE, REPLY, FORWARD * Moving messages: COPY, MOVE * Compound commands: SET and SHOW Loading @@ -42,12 +42,18 @@ repl.commands? * Using giu, a [text-editor](https://serge-hulne.medium.com/coding-a-simple-text-editor-in-go-using-giu-quick-and-dirty-b9b97ab41e4a) (needs cgo, no) * [bubbletea](https://github.com/charmbracelet/bubbletea) seems to be the tui that's winning * Another option is tview - [simpler](https://github.com/rivo/tview). * Handle broadcast messages - have bulletin watch a directory and display files from it. Then have them delete the file if it's older than 5 minutes (allow for failure) * Cleanup help output. * Remove the node/cluster/newsgroup/mailing-list related flags. * Remove BBOARD references. * format with `par w72j1` * Database * trigger to limit values for 'visibility'? * Add some of the early announcements from the sources - see the conversion branch - to the GENERAL folder. * Add a pager * Add commands: * A way to add / delete ssh keys. * A way to manage files? Loading
accounts/accounts.go +12 −3 Original line number Diff line number Diff line Loading @@ -56,12 +56,10 @@ func Open(login string) error { user.Admin = 0 fmt.Printf("Welcome new user %s\n", login) rl, err := readline.New("please enter your name: ") user.Name, err = GetLine("please enter your name: ") if err != nil { return err } user.Name, err = rl.Readline() rl.Close() err = User.Folders.AddUser(*user) if err != nil { Loading @@ -84,3 +82,14 @@ func Open(login string) error { func (u *UserData) Close() { u.Folders.Close() } // GetLine gets a line. func GetLine(prompt string) (string, error) { rl, err := readline.New(prompt) if err != nil { return "", err } defer rl.Close() line, err := rl.Readline() return line, err }
folders/messages.go +60 −3 Original line number Diff line number Diff line // Package folders are all the routines and sql for managing folders. package folders import "time" import ( "fmt" "strings" "time" ) // CreateMessage creates a new folder. func (s *Store) CreateMessage(author, subject, message, folder string, permanent, shutdown int, expiration *time.Time) error { if expiration == nil { var days int err := s.db.Get(&days, "SELECT expire FROM folders WHERE name = $1", folder) if err != nil { return err } if days <= 0 { days = 14 } exp := time.Now().AddDate(0, 0, days) expiration = &exp } // TODO: replace _ with rows and check. _, err := s.db.Exec( `INSERT INTO messages (id, folder, author, subject, message, permanent, shutdown, expiration) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`, 1, // TODO: how to set this. ((SELECT COALESCE(MAX(id), 0) + 1 FROM messages WHERE folder = $1), $1, $2, $3, $4, $5, $6, $7)`, folder, author, subject, Loading @@ -22,3 +38,44 @@ func (s *Store) CreateMessage(author, subject, message, folder string, permanent // TODO: process this error a bit more to give a better error message. return err } // Message contains a message type Message struct { ID int `db:"id"` Folder string `db:"folder"` Author string `db:"author"` Subject string `db:"subject"` Message string `db:"message"` Expires time.Time `db:"expiration"` CreateAt time.Time `db:"create_at"` UpdateAt time.Time `db:"update_at"` } // String renders a message. func (m *Message) String() string { buf := &strings.Builder{} // TODO: Show if an edit has happened. fmt.Fprintf(buf, "From: \"%s\" %s\n", m.Author, m.CreateAt.Format("02-JAN-2006 15:04:05")) fmt.Fprintf(buf, "To: %s\n", m.Folder) fmt.Fprintf(buf, "Subj: %s\n\n", m.Subject) fmt.Fprintf(buf, "%s\n", m.Message) return buf.String() } // ReadMessage reads a message for a user. func (s *Store) ReadMessage(login, folder string, msgid int) (*Message, error) { msg := &Message{} err := s.db.Get(msg, `SELECT id, folder, author, subject, message, expiration, create_at, update_at FROM messages WHERE folder = $1, id = $2`, folder, msgid) if err != nil { return nil, err } // TODO: replace _ with rows and check. _, err = s.db.Exec( "INSERT INTO read (login, folder, msgid) VALUES ($1, $2, $3)", login, folder, msgid) return msg, err }
folders/sql/1_create_table.up.sql +11 −0 Original line number Diff line number Diff line Loading @@ -117,3 +117,14 @@ CREATE TABLE messages ( CREATE INDEX messages_idx_shutdown ON messages(shutdown); CREATE INDEX messages_idx_expiration ON messages(expiration); CREATE TABLE read ( login VARCHAR(25) REFERENCES users(login) ON DELETE CASCADE ON UPDATE CASCADE, folder VARCHAR(25) REFERENCES folders(name) ON DELETE CASCADE ON UPDATE CASCADE, msgid INT, PRIMARY KEY (folder, login, msgid), CONSTRAINT FK_id_folder FOREIGN KEY (msgid, folder) REFERENCES messages(id, folder) ON DELETE CASCADE ON UPDATE CASCADE ) WITHOUT ROWID;