1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
|
// Infodump is a commandline social network that works over IPFS and communicates through PubSub
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 {
reader := bufio.NewReader(os.Stdin)
line, _ := reader.ReadString('\n')
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"
// 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
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)
}
}
}
|