diff options
author | Joop Kiefte <ikojba@gmail.com> | 2021-12-11 00:20:21 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-12-11 00:20:21 +0000 |
commit | 7f5a5cd7487faea09fd13caba0303dcf4ade0f85 (patch) | |
tree | 6466e03be021fc5b7ef2aed71b71781b8cfcfcbc | |
parent | ae90867ca38b0bf1d8d51522a5437068758ef4c0 (diff) |
Get database, uploads and menu working
-rw-r--r-- | main.go | 282 | ||||
-rw-r--r-- | message/message.go | 15 |
2 files changed, 246 insertions, 51 deletions
@@ -3,14 +3,21 @@ package main import ( "bufio" + "database/sql" "fmt" + "net/url" "os" + "strings" + "time" "git.kiefte.eu/lapingvino/infodump/message" + ipfs "github.com/ipfs/go-ipfs-api" _ "modernc.org/sqlite" ) var LocalMessages = message.Messages{} +var DatabasePath = "infodump.db" +var DB *sql.DB // Readline reads from a buffered stdin and returns the line func Readline() string { @@ -19,56 +26,243 @@ func Readline() string { return line } +// MenuElements is a list of options for the menu, consisting of a description and a function +type MenuElements struct { + Description string + Function func() +} + +// Menu presents a menu to the user +func Menu(menu []MenuElements) { + // Present the user with a menu + fmt.Println("Welcome to Infodump") + for i, e := range menu { + fmt.Println(i+1, e.Description) + } + fmt.Println("Enter your choice: ") + var choice int + fmt.Scanln(&choice) + if choice > 0 && choice <= len(menu) { + menu[choice-1].Function() + } else { + fmt.Println("Invalid Choice") + } +} + func main() { // Set message IPFS client to use the local IPFS node message.IPFSGateway = "http://localhost:5001" - // Run a loop and present a menu to the user to read messages, write messages or quit the program + // If the first argument to the command is a valid link, use that for the IPFSGateway instead + if len(os.Args) > 1 { + u, err := url.Parse(os.Args[1]) + if err == nil { + message.IPFSGateway = u.String() + } + } + + err := TestIPFSGateway(message.IPFSGateway) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + // Run a loop and present a menu to the user to + // read messages + // write messages + // sync messages + // set the IPFS gateway + // set the database + // configure the followed tags + // quit the program for { // Present the user with a menu - fmt.Println("Welcome to Infodump") - fmt.Println("1. Read Messages") - fmt.Println("2. Write Messages") - fmt.Println("3. Sync messages") - fmt.Println("4. Quit") - fmt.Println("Enter your choice: ") - var choice int - fmt.Scanln(&choice) - switch choice { - case 1: - // Read the messages from PubSub topic "OLN" - fmt.Println("Reading messages...") - // TODO: Read messages from PubSub topic "OLN" - case 2: - // Write Messages - fmt.Println("Writing Messages") - // Get a message and an urgency from the user. - // The urgency is used to set the strength of the Proof of Work - fmt.Println("Enter a message: ") - m := Readline() - fmt.Println("Enter an urgency (higher is stronger but takes longer to produce): ") - var urgency int - fmt.Scanln(&urgency) - // Create a Message object - msg := message.New(m, urgency) - // Add the message to LocalMessages - LocalMessages.Add(msg) - case 3: - // Sync messages - fmt.Println("Syncing messages...") - // Upload the LocalMessages to IPFS - hash, err := LocalMessages.AddToIPFS() - if err != nil { - fmt.Println(err) - } - // TODO: Publish the hash to the PubSub topic "OLN" - fmt.Println("Hash: ", hash) - case 4: - // Quit - fmt.Println("Quitting") - os.Exit(0) - default: - fmt.Println("Invalid Choice") + Menu([]MenuElements{ + {"Read Messages", ReadMessages}, + {"Write Message", WriteMessage}, + {"Sync Messages", SyncMessages}, + {"Set IPFS Gateway", SetIPFSGateway}, + {"Set Database", SetDatabase}, + {"Configure Followed Tags", ConfigureFollowedTags}, + {"Quit", func() { os.Exit(0) }}, + }) + + // After the user has selected an option and before the user gets to chose again, ask to press enter to continue + fmt.Println("Press enter to continue...") + Readline() + } +} + +// GetDatabase checks if DB is already set and opened, if not it Sets the database first +func GetDatabase() *sql.DB { + if DB == nil { + fmt.Println("Database not set, setting database...") + SetDatabase() + } + return DB +} + +func ReadMessages() { + // TODO: Read messages from PubSub topic "OLN" + fmt.Println("Reading messages...") +} + +func WriteMessage() { + // Get a message and an urgency from the user. + // The urgency is used to set the strength of the Proof of Work + fmt.Println("Enter a message: ") + m := Readline() + fmt.Println("Enter an urgency (higher is stronger but takes longer to produce): ") + var urgency int + fmt.Scanln(&urgency) + // Create a Message object + msg := message.New(m, urgency, time.Now().Unix()) + // Add the message to LocalMessages + LocalMessages.Add(msg) +} + +func SyncMessages() { + // Update the database with the messages in LocalMessages + db := GetDatabase() + // Put the messages in the database + for k, m := range LocalMessages { + _, err := db.Exec("INSERT INTO messages(hash, message, nonce, timestamp) VALUES(?,?,?,?)", m.Stamp(), m.Message, m.Nonce, m.Timestamp) + if err != nil { + fmt.Println(err) + } else { + fmt.Println("Message", k, "synced") + } + } + + fmt.Println("Now loading messages from database...") + // Get the messages from the database in a new Messages object + var messages = message.Messages{} + rows, err := db.Query("SELECT * FROM messages") + if err != nil { + fmt.Println(err) + } + for rows.Next() { + var hash, msg string + var nonce int + var timestamp int64 + fmt.Println("Reading message...") + err := rows.Scan(&hash, &msg, &nonce, ×tamp) + if err != nil { + fmt.Println(err) + } + fmt.Println("Message", hash, "loaded") + + m := message.Message{ + Message: msg, + Nonce: nonce, + Timestamp: timestamp, + } + + messages[hash] = &m + } + fmt.Println("Messages loaded from database") + // Add the messages to the IPFS network + cid, err := messages.AddToIPFS() + if err != nil { + fmt.Println(err) + } else { + fmt.Println("Messages synced to IPFS: ", cid) + } +} + +func TestIPFSGateway(gateway string) error { + // Test the IPFS gateway + fmt.Println("Testing IPFS gateway...") + ipfs := ipfs.NewShell(gateway) + _, err := ipfs.ID() + return err +} + +func SetIPFSGateway() { + // Set the IPFS gateway + fmt.Println("Enter the IPFS gateway: ") + var gateway string + fmt.Scanln(&gateway) + // Test the IPFS gateway + err := TestIPFSGateway(gateway) + if err != nil { + fmt.Println(err) + return + } else { + fmt.Println("IPFS gateway set to: ", gateway) + message.IPFSGateway = gateway + } +} + +func InitDatabase(db *sql.DB) { + var err error + // Create the table "messages" + _, err = db.Exec("CREATE TABLE messages(hash TEXT PRIMARY KEY, message TEXT, nonce INTEGER, timestamp INTEGER)") + if err != nil { + fmt.Println(err) + } + // Create the table "followed_tags" + _, err = db.Exec("CREATE TABLE followed_tags(tag TEXT)") + if err != nil { + fmt.Println(err) + } +} + +// SetDatabase configures DB to be the database to use +// The name used is DatabasePath, but the user will be asked if this correct or if they want to change it +// If the database is already set, it will ask the user if they want to overwrite it +// If the database is not set, it will ask the user if they want to create it +// If the database is set but it doesn't contain the tables "messages" and "followed_tags", +// it will create them +func SetDatabase() { + // First check if the user is okay with the database path + fmt.Println("Database path: ", DatabasePath) + fmt.Println("Is this correct? (y/n)") + answer := Readline() + if strings.HasPrefix(answer, "n") { + fmt.Println("Enter the database path: ") + DatabasePath = Readline() + } + // Open the database + db, err := sql.Open("sqlite", DatabasePath) + if err != nil { + fmt.Println(err) + return + } + // Check if the database is already set + if DB != nil { + fmt.Println("Database already set, overwrite? (y/n)") + answer := Readline() + if strings.HasPrefix(answer, "n") { + return + } + } + // Check if the database exists + if _, err := os.Stat(DatabasePath); os.IsNotExist(err) { + fmt.Println("Database does not exist, create? (y/n)") + answer := Readline() + if strings.HasPrefix(answer, "n") { + return + } + } + // Set the database + DB = db + // Check if the database contains the tables "messages" and "followed_tags" + // If not, create them + InitDatabase(DB) +} + +func ConfigureFollowedTags() { + db := GetDatabase() + fmt.Println("Enter the tags you want to follow, separated by spaces: ") + tags := Readline() + // Split the tags into an array and insert them into database DB + // using table "followed_tags" + tagArray := strings.Split(tags, " ") + for _, tag := range tagArray { + _, err := db.Exec("INSERT INTO followed_tags(tag) VALUES(?)", tag) + if err != nil { + fmt.Println(err) } } } diff --git a/message/message.go b/message/message.go index 507a4e7..19f04a5 100644 --- a/message/message.go +++ b/message/message.go @@ -20,8 +20,9 @@ func InitIPFS() *ipfs.Shell { // Messages on Infodump use a "stamp" using the hashcash algorithm to prevent spam and enable storing messages by importance // The Message type contains the message itself and a nonce that is used to verify the stamp type Message struct { - Message string - Nonce int + Message string + Timestamp int64 + Nonce int } // Proof of Work: Find the nonce for a message by hashing the message and checking for at least n initial zeroes in the binary representation of the resulting hash @@ -43,9 +44,9 @@ func (msg *Message) ProofOfWork(n int) { *msg = m } -// Get the SHA256 hash of a message plus the nonce as a byte slice +// Get the SHA256 hash of a message plus the timestamp plus the nonce as a byte slice func (m *Message) Hash() [32]byte { - hash := sha256.Sum256([]byte(fmt.Sprintf("%s%d", m.Message, m.Nonce))) + hash := sha256.Sum256([]byte(m.Message + fmt.Sprintf("%d", m.Timestamp) + fmt.Sprintf("%d", m.Nonce))) return hash } @@ -76,8 +77,8 @@ func (m *Message) Lead() int { return CountLeadingZeroes(m.Hash()) } -func New(msg string, n int) *Message { - m := Message{Message: msg} +func New(msg string, n int, timestamp int64) *Message { + m := Message{Message: msg, Timestamp: timestamp} m.ProofOfWork(n) return &m } @@ -112,7 +113,7 @@ func (m *Messages) AddToIPFS() (string, error) { func AddJSONToIPFS(json []byte) (string, error) { // Create an IPFS instance based on the IPFSGateway myIPFS := ipfs.NewShell(IPFSGateway) - // Turn the JSON into a DAG node and return the CID + // Turn the JSON into a Reader and add it to IPFS cid, err := myIPFS.Add(bytes.NewReader(json)) if err != nil { return "", err |