|
|
|
@ -3,23 +3,14 @@ package main
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bufio"
|
|
|
|
|
"database/sql"
|
|
|
|
|
"fmt"
|
|
|
|
|
"net/url"
|
|
|
|
|
"os"
|
|
|
|
|
"strings"
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"git.kiefte.eu/lapingvino/infodump/message"
|
|
|
|
|
shell "github.com/ipfs/go-ipfs-api"
|
|
|
|
|
_ "modernc.org/sqlite"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var LocalMessages = message.Messages{}
|
|
|
|
|
var DatabasePath = "infodump.db"
|
|
|
|
|
var DB *sql.DB
|
|
|
|
|
var MessageCache string
|
|
|
|
|
|
|
|
|
|
// Readline reads from a buffered stdin and returns the line
|
|
|
|
|
func Readline() string {
|
|
|
|
|
reader := bufio.NewReader(os.Stdin)
|
|
|
|
@ -112,367 +103,3 @@ func SettingsMenu() {
|
|
|
|
|
{"Back", func() {}},
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// A menu for the several parts of Sync:
|
|
|
|
|
// saving messages to the local database
|
|
|
|
|
// reading messages from the local database
|
|
|
|
|
// reading messages from the network
|
|
|
|
|
// writing messages to the network
|
|
|
|
|
func SyncMenu() {
|
|
|
|
|
// Present the user with a menu
|
|
|
|
|
Menu([]MenuElements{
|
|
|
|
|
{"Save Messages to Database", SaveMessagesToDatabase},
|
|
|
|
|
{"Read Messages from Database", ReadMessagesFromDatabase},
|
|
|
|
|
{"Read Messages from Network", ReadMessagesFromNetwork},
|
|
|
|
|
{"Write Messages to Network", WriteMessagesToNetwork},
|
|
|
|
|
{"Back", func() {}},
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// StartOLNListener starts a PubSub listener that listens for messages from the network
|
|
|
|
|
// and adds them to LocalMessages
|
|
|
|
|
// For this it uses the IPFS gateway and listens on the topic "OLN", as well as
|
|
|
|
|
// the list of followed tags from the database
|
|
|
|
|
func StartOLNListener() {
|
|
|
|
|
// Get the IPFS gateway
|
|
|
|
|
gateway := message.IPFSGateway
|
|
|
|
|
// Get the database
|
|
|
|
|
db := GetDatabase()
|
|
|
|
|
// Get the list of followed tags
|
|
|
|
|
followedTags := GetFollowedTags(db)
|
|
|
|
|
// Create a new IPFS client
|
|
|
|
|
myIPFS := shell.NewShell(gateway)
|
|
|
|
|
var subs []*shell.PubSubSubscription
|
|
|
|
|
olnsub, err := myIPFS.PubSubSubscribe("OLN")
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Println(err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
subs = append(subs, olnsub)
|
|
|
|
|
for _, tag := range followedTags {
|
|
|
|
|
tagssub, err := myIPFS.PubSubSubscribe("oln-" + tag)
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Println(err)
|
|
|
|
|
} else {
|
|
|
|
|
subs = append(subs, tagssub)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Start a goroutine for each of the subscriptions in subs,
|
|
|
|
|
// read the CID from the Next method, look up the CID on IPFS,
|
|
|
|
|
// read this in via message.MessagesFromIPFS and add the message to LocalMessages
|
|
|
|
|
for _, sub := range subs {
|
|
|
|
|
go func(sub *shell.PubSubSubscription) {
|
|
|
|
|
for {
|
|
|
|
|
msg, err := sub.Next()
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Println(err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
msgs, err := message.MessagesFromIPFS(string(msg.Data))
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Println(err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
LocalMessages.AddMany(msgs)
|
|
|
|
|
}
|
|
|
|
|
}(sub)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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 GetMessagesFromDatabase(db *sql.DB) *message.Messages {
|
|
|
|
|
// Get all messages from the database
|
|
|
|
|
rows, err := db.Query("SELECT hash, message, nonce, timestamp FROM messages")
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Println(err)
|
|
|
|
|
}
|
|
|
|
|
defer rows.Close()
|
|
|
|
|
// Create a new Messages object
|
|
|
|
|
msgs := message.Messages{}
|
|
|
|
|
// Loop through all messages
|
|
|
|
|
fmt.Println("Getting messages from database...")
|
|
|
|
|
for rows.Next() {
|
|
|
|
|
var hash, msg string
|
|
|
|
|
var nonce int
|
|
|
|
|
var timestamp int64
|
|
|
|
|
// Get the values from the database
|
|
|
|
|
err := rows.Scan(&hash, &msg, &nonce, ×tamp)
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Println(err)
|
|
|
|
|
}
|
|
|
|
|
fmt.Println("Got message from database:", hash)
|
|
|
|
|
|
|
|
|
|
// Create a new message object
|
|
|
|
|
m := message.Message{
|
|
|
|
|
Message: msg,
|
|
|
|
|
Nonce: nonce,
|
|
|
|
|
Timestamp: timestamp,
|
|
|
|
|
}
|
|
|
|
|
// Add the message to the Messages object
|
|
|
|
|
fmt.Println("Adding message to Messages object...")
|
|
|
|
|
msgs.Add(&m)
|
|
|
|
|
}
|
|
|
|
|
return &msgs
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ReadMessages shows the messages in LocalMessages sorted by importance, 10 at a time
|
|
|
|
|
func ReadMessages() {
|
|
|
|
|
// Use LocalMessages to get the messages and get the sorted list of messages
|
|
|
|
|
// through the MessageList method
|
|
|
|
|
msgs := LocalMessages.MessageList()
|
|
|
|
|
// Loop through the messages and print them
|
|
|
|
|
for i, m := range msgs {
|
|
|
|
|
fmt.Println(m)
|
|
|
|
|
if i%10 == 9 {
|
|
|
|
|
fmt.Println("Press enter to continue... Type anything to stop")
|
|
|
|
|
contp := Readline()
|
|
|
|
|
if contp != "" {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func WriteMessage() {
|
|
|
|
|
// Get a message and an urgency from the user.
|
|
|
|
|
// The urgency is used to set the strength of the Proof of Work
|
|
|
|
|
// If there is a message in the MessageCache, ask the user if they want to use it
|
|
|
|
|
// If there is no message in the MessageCache, ask the user to write a message
|
|
|
|
|
var m string
|
|
|
|
|
if MessageCache != "" {
|
|
|
|
|
fmt.Println("You wrote a message before that you didn't send yet. Do you want to use that message?")
|
|
|
|
|
fmt.Println("The message is:", MessageCache)
|
|
|
|
|
fmt.Println("Type 'yes' to use the message, or anything else to write a new message")
|
|
|
|
|
usep := Readline()
|
|
|
|
|
if usep == "yes" {
|
|
|
|
|
fmt.Println("Using message from cache")
|
|
|
|
|
m = MessageCache
|
|
|
|
|
} else {
|
|
|
|
|
fmt.Println("Writing new message")
|
|
|
|
|
m = usep
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
fmt.Println("Write a message:")
|
|
|
|
|
m = Readline()
|
|
|
|
|
}
|
|
|
|
|
fmt.Println("Enter an urgency (higher is stronger but takes longer to produce): ")
|
|
|
|
|
var urgency int
|
|
|
|
|
fmt.Scanln(&urgency)
|
|
|
|
|
fmt.Println("How many seconds should we wait for the POW to be done? (default is 5): ")
|
|
|
|
|
var powtime int
|
|
|
|
|
fmt.Scanln(&powtime)
|
|
|
|
|
if powtime == 0 {
|
|
|
|
|
powtime = 5
|
|
|
|
|
}
|
|
|
|
|
// Create a new message object
|
|
|
|
|
msg, err := message.New(m, urgency, time.Now().Unix(), time.Duration(powtime)*time.Second)
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Println(err)
|
|
|
|
|
fmt.Println("Want to try again with another urgency or timeout? (y/n)")
|
|
|
|
|
contp := Readline()
|
|
|
|
|
if contp == "y" {
|
|
|
|
|
MessageCache = m
|
|
|
|
|
WriteMessage()
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
// MessageCache can be discarded after this point
|
|
|
|
|
MessageCache = ""
|
|
|
|
|
// Add the message to LocalMessages
|
|
|
|
|
LocalMessages.Add(msg)
|
|
|
|
|
|
|
|
|
|
// Ask if the user wants to write another message or save the messages to the database
|
|
|
|
|
fmt.Println("Do you want to write another message? (y/n)")
|
|
|
|
|
contp := Readline()
|
|
|
|
|
if contp == "y" {
|
|
|
|
|
WriteMessage()
|
|
|
|
|
} else {
|
|
|
|
|
fmt.Println("Do you want to save the messages to the database? (y/n)")
|
|
|
|
|
contp := Readline()
|
|
|
|
|
if contp == "y" {
|
|
|
|
|
SaveMessagesToDatabase()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Implementing the Sync Menu options
|
|
|
|
|
// SaveMessagesToDatabase, ReadMessagesFromDatabase, ReadMessagesFromNetwork and WriteMessagesToNetwork
|
|
|
|
|
|
|
|
|
|
// SaveMessagesToDatabase saves the messages in LocalMessages to the database
|
|
|
|
|
func SaveMessagesToDatabase() {
|
|
|
|
|
// Update the database with the messages in LocalMessages
|
|
|
|
|
db := GetDatabase()
|
|
|
|
|
// Put the messages in the database
|
|
|
|
|
LocalMessages.Each(func(m *message.Message) {
|
|
|
|
|
_, 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", m.Stamp(), "saved to database")
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ReadMessagesFromDatabase reads the messages from the database and adds them to LocalMessages
|
|
|
|
|
func ReadMessagesFromDatabase() {
|
|
|
|
|
// Get the messages from the database
|
|
|
|
|
messages := GetMessagesFromDatabase(GetDatabase())
|
|
|
|
|
// Add the messages to LocalMessages
|
|
|
|
|
LocalMessages.AddMany(messages)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ReadMessagesFromNetwork reads the messages from the IPFS network and adds them to LocalMessages
|
|
|
|
|
func ReadMessagesFromNetwork() {
|
|
|
|
|
// Get the messages from the IPFS network
|
|
|
|
|
// TODO: Implement this
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// WriteMessagesToNetwork writes the messages in LocalMessages to the IPFS network
|
|
|
|
|
func WriteMessagesToNetwork() {
|
|
|
|
|
// Add the messages to the IPFS network
|
|
|
|
|
cid, err := LocalMessages.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 := shell.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 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 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 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("At the moment you follow the following tags:")
|
|
|
|
|
// Get the tags that the user is following
|
|
|
|
|
tags := GetFollowedTags(db)
|
|
|
|
|
// Show the tags
|
|
|
|
|
for _, tag := range tags {
|
|
|
|
|
fmt.Print(tag, " ")
|
|
|
|
|
}
|
|
|
|
|
fmt.Println()
|
|
|
|
|
fmt.Println("Enter the tags you want to follow, separated by spaces\nTo remove tags, prefix them with a minus sign: ")
|
|
|
|
|
newtags := Readline()
|
|
|
|
|
// Split the tags into an array and insert them into database DB
|
|
|
|
|
// using table "followed_tags"
|
|
|
|
|
tagArray := strings.Split(newtags, " ")
|
|
|
|
|
for _, tag := range tagArray {
|
|
|
|
|
tag = strings.Trim(tag, " \n")
|
|
|
|
|
if !strings.HasPrefix(tag, "-") {
|
|
|
|
|
_, err := db.Exec("INSERT INTO followed_tags(tag) VALUES(?)", tag)
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Println(err)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
_, err := db.Exec("DELETE FROM followed_tags WHERE tag=?", tag[1:])
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Println(err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func GetFollowedTags(db *sql.DB) []string {
|
|
|
|
|
// Get the tags from the database
|
|
|
|
|
rows, err := db.Query("SELECT tag FROM followed_tags")
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Println(err)
|
|
|
|
|
}
|
|
|
|
|
var tags []string
|
|
|
|
|
for rows.Next() {
|
|
|
|
|
var tag string
|
|
|
|
|
err = rows.Scan(&tag)
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Println(err)
|
|
|
|
|
}
|
|
|
|
|
tags = append(tags, tag)
|
|
|
|
|
}
|
|
|
|
|
return tags
|
|
|
|
|
}
|
|
|
|
|