Skip to content

Commit 776ba27

Browse files
authored
Merge pull request #8 from TheDiscordian/main
Improvements to the Go daemon
2 parents 7d5658b + 3e18e10 commit 776ba27

File tree

5 files changed

+118
-23
lines changed

5 files changed

+118
-23
lines changed

go-server/.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
identity.key

go-server/chatroom.go

+5-16
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package main
22

33
import (
44
"context"
5-
"encoding/json"
65

76
"github.com/libp2p/go-libp2p/core/peer"
87

@@ -18,6 +17,7 @@ const ChatRoomBufSize = 128
1817
type ChatRoom struct {
1918
// Messages is a channel of messages received from other peers in the chat room
2019
Messages chan *ChatMessage
20+
SysMessages chan *ChatMessage
2121

2222
ctx context.Context
2323
ps *pubsub.PubSub
@@ -60,6 +60,7 @@ func JoinChatRoom(ctx context.Context, ps *pubsub.PubSub, selfID peer.ID, nickna
6060
nick: nickname,
6161
roomName: roomName,
6262
Messages: make(chan *ChatMessage, ChatRoomBufSize),
63+
SysMessages: make(chan *ChatMessage, ChatRoomBufSize),
6364
}
6465

6566
// start reading messages from the subscription in a loop
@@ -69,16 +70,7 @@ func JoinChatRoom(ctx context.Context, ps *pubsub.PubSub, selfID peer.ID, nickna
6970

7071
// Publish sends a message to the pubsub topic.
7172
func (cr *ChatRoom) Publish(message string) error {
72-
m := ChatMessage{
73-
Message: message,
74-
SenderID: cr.self.Pretty(),
75-
SenderNick: cr.nick,
76-
}
77-
msgBytes, err := json.Marshal(m)
78-
if err != nil {
79-
return err
80-
}
81-
return cr.topic.Publish(cr.ctx, msgBytes)
73+
return cr.topic.Publish(cr.ctx, []byte(message))
8274
}
8375

8476
func (cr *ChatRoom) ListPeers() []peer.ID {
@@ -99,12 +91,9 @@ func (cr *ChatRoom) readLoop() {
9991
}
10092
cm := new(ChatMessage)
10193
cm.Message = string(msg.Data)
102-
cm.SenderID = string(msg.ID)
103-
cm.SenderNick = "unknown"
94+
cm.SenderID = msg.ID
95+
cm.SenderNick = string(msg.ID[len(msg.ID)-8])
10496

105-
if err != nil {
106-
continue
107-
}
10897
// send valid messages onto the Messages channel
10998
cr.Messages <- cm
11099
}

go-server/identity.go

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package main
2+
// Borrowed from https://github.com/libp2p/go-libp2p-relay-daemon/blob/master/identity.go
3+
4+
import (
5+
"fmt"
6+
"os"
7+
8+
"github.com/libp2p/go-libp2p/core/crypto"
9+
)
10+
11+
// LoadIdentity reads a private key from the given path and, if it does not
12+
// exist, generates a new one.
13+
func LoadIdentity(idPath string) (crypto.PrivKey, error) {
14+
if _, err := os.Stat(idPath); err == nil {
15+
return ReadIdentity(idPath)
16+
} else if os.IsNotExist(err) {
17+
fmt.Printf("Generating peer identity in %s\n", idPath)
18+
return GenerateIdentity(idPath)
19+
} else {
20+
return nil, err
21+
}
22+
}
23+
24+
// ReadIdentity reads a private key from the given path.
25+
func ReadIdentity(path string) (crypto.PrivKey, error) {
26+
bytes, err := os.ReadFile(path)
27+
if err != nil {
28+
return nil, err
29+
}
30+
31+
return crypto.UnmarshalPrivateKey(bytes)
32+
}
33+
34+
// GenerateIdentity writes a new random private key to the given path.
35+
func GenerateIdentity(path string) (crypto.PrivKey, error) {
36+
privk, _, err := crypto.GenerateKeyPair(crypto.Ed25519, 0)
37+
if err != nil {
38+
return nil, err
39+
}
40+
41+
bytes, err := crypto.MarshalPrivateKey(privk)
42+
if err != nil {
43+
return nil, err
44+
}
45+
46+
err = os.WriteFile(path, bytes, 0400)
47+
48+
return privk, err
49+
}

go-server/main.go

+33-5
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"context"
55
"flag"
66
"fmt"
7+
"io/ioutil"
8+
"log"
79
"os"
810
"sync"
911
"time"
@@ -28,7 +30,7 @@ const DiscoveryInterval = time.Hour
2830
// DiscoveryServiceTag is used in our mDNS advertisements to discover other chat peers.
2931
const DiscoveryServiceTag = "universal-connectivity"
3032

31-
var ChatMsgChan chan *ChatMessage
33+
var SysMsgChan chan *ChatMessage
3234

3335
// Borrowed from https://medium.com/rahasak/libp2p-pubsub-peer-discovery-with-kademlia-dht-c8b131550ac7
3436
// NewDHT attempts to connect to a bunch of bootstrap peers and returns a new DHT.
@@ -76,7 +78,7 @@ func Discover(ctx context.Context, h host.Host, dht *dht.IpfsDHT, rendezvous str
7678

7779
discovery.Advertise(ctx, routingDiscovery, rendezvous)
7880

79-
ticker := time.NewTicker(time.Second * 1)
81+
ticker := time.NewTicker(time.Second * 10)
8082
defer ticker.Stop()
8183

8284
for {
@@ -108,19 +110,45 @@ func Discover(ctx context.Context, h host.Host, dht *dht.IpfsDHT, rendezvous str
108110
}
109111

110112
func LogMsgf(f string, msg ...any) {
111-
ChatMsgChan <- &ChatMessage{Message: fmt.Sprintf(f, msg...), SenderID: "system", SenderNick: "system"}
113+
SysMsgChan <- &ChatMessage{Message: fmt.Sprintf(f, msg...), SenderID: "system", SenderNick: "system"}
112114
}
113115

114116
func main() {
115117
// parse some flags to set our nickname and the room to join
116118
nickFlag := flag.String("nick", "", "nickname to use in chat. will be generated if empty")
117119
roomFlag := flag.String("room", "universal-connectivity", "name of chat room to join")
120+
idPath := flag.String("identity", "identity.key", "path to the private key (PeerID) file")
121+
useLogger := flag.Bool("logger", false, "write logs to file")
118122
flag.Parse()
119123

124+
if *useLogger {
125+
f, err := os.OpenFile("log.txt", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
126+
if err != nil {
127+
log.Println("failed to open log file", err)
128+
log.SetOutput(ioutil.Discard)
129+
} else {
130+
defer f.Close()
131+
log.SetOutput(f)
132+
}
133+
} else {
134+
log.SetOutput(ioutil.Discard)
135+
}
136+
120137
ctx := context.Background()
121138

139+
// load our private key to generate the same peerID each time
140+
privk, err := LoadIdentity(*idPath)
141+
if err != nil {
142+
panic(err)
143+
}
144+
122145
// create a new libp2p Host that listens on a random TCP port
123-
h, err := libp2p.New(libp2p.Transport(quicTransport.NewTransport), libp2p.Transport(webtransport.New), libp2p.ListenAddrStrings("/ip4/0.0.0.0/udp/0/quic-v1", "/ip4/0.0.0.0/udp/0/quic-v1/webtransport", "/ip6/::/udp/0/quic-v1", "/ip6/::/udp/0/quic-v1/webtransport"))
146+
h, err := libp2p.New(
147+
libp2p.Identity(privk),
148+
libp2p.Transport(quicTransport.NewTransport),
149+
libp2p.Transport(webtransport.New),
150+
libp2p.ListenAddrStrings("/ip4/0.0.0.0/udp/0/quic-v1", "/ip4/0.0.0.0/udp/0/quic-v1/webtransport", "/ip6/::/udp/0/quic-v1", "/ip6/::/udp/0/quic-v1/webtransport"),
151+
)
124152
if err != nil {
125153
panic(err)
126154
}
@@ -145,7 +173,7 @@ func main() {
145173
if err != nil {
146174
panic(err)
147175
}
148-
ChatMsgChan = cr.Messages
176+
SysMsgChan = cr.SysMessages
149177

150178
// setup DHT with empty discovery peers
151179
// so this will be a discovery peer for others

go-server/ui.go

+30-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package main
33
import (
44
"fmt"
55
"io"
6+
"log"
67
"time"
78

89
"github.com/gdamore/tcell/v2"
@@ -19,6 +20,7 @@ type ChatUI struct {
1920
peersList *tview.TextView
2021

2122
msgW io.Writer
23+
sysW io.Writer
2224
inputCh chan string
2325
doneCh chan struct{}
2426
}
@@ -41,6 +43,19 @@ func NewChatUI(cr *ChatRoom) *ChatUI {
4143
app.Draw()
4244
})
4345

46+
// make a text view to contain our error messages
47+
sysBox := tview.NewTextView()
48+
sysBox.SetDynamicColors(true)
49+
sysBox.SetBorder(true)
50+
sysBox.SetTitle("System")
51+
52+
// text views are io.Writers, but they don't automatically refresh.
53+
// this sets a change handler to force the app to redraw when we get
54+
// new messages to display.
55+
sysBox.SetChangedFunc(func() {
56+
app.Draw()
57+
})
58+
4459
// an input field for typing messages into
4560
inputCh := make(chan string, 32)
4661
input := tview.NewInputField().
@@ -87,8 +102,9 @@ func NewChatUI(cr *ChatRoom) *ChatUI {
87102

88103
flex := tview.NewFlex().
89104
SetDirection(tview.FlexRow).
90-
AddItem(chatPanel, 0, 1, false).
91-
AddItem(input, 1, 1, true)
105+
AddItem(chatPanel, 0, 3, false).
106+
AddItem(sysBox, 0, 2, false).
107+
AddItem(input, 2, 1, true)
92108

93109
app.SetRoot(flex, true)
94110

@@ -97,6 +113,7 @@ func NewChatUI(cr *ChatRoom) *ChatUI {
97113
app: app,
98114
peersList: peersList,
99115
msgW: msgBox,
116+
sysW: sysBox,
100117
inputCh: inputCh,
101118
doneCh: make(chan struct{}, 1),
102119
}
@@ -138,6 +155,13 @@ func (ui *ChatUI) displayChatMessage(cm *ChatMessage) {
138155
fmt.Fprintf(ui.msgW, "%s %s\n", prompt, cm.Message)
139156
}
140157

158+
// displayChatMessage writes a ChatMessage from the room to the message window,
159+
// with the sender's nick highlighted in green.
160+
func (ui *ChatUI) displaySysMessage(cm *ChatMessage) {
161+
fmt.Fprintf(ui.sysW, "%s\n", cm.Message)
162+
log.Println(cm.Message)
163+
}
164+
141165
// displaySelfMessage writes a message from ourself to the message window,
142166
// with our nick highlighted in yellow.
143167
func (ui *ChatUI) displaySelfMessage(msg string) {
@@ -166,6 +190,10 @@ func (ui *ChatUI) handleEvents() {
166190
// when we receive a message from the chat room, print it to the message window
167191
ui.displayChatMessage(m)
168192

193+
case s := <-ui.cr.SysMessages:
194+
// when we receive a message from the chat room, print it to the message window
195+
ui.displaySysMessage(s)
196+
169197
case <-peerRefreshTicker.C:
170198
// refresh the list of peers in the chat room periodically
171199
ui.refreshPeers()

0 commit comments

Comments
 (0)