Skip to content

Commit

Permalink
Merge pull request #8 from TheDiscordian/main
Browse files Browse the repository at this point in the history
Improvements to the Go daemon
  • Loading branch information
2color authored Mar 30, 2023
2 parents 7d5658b + 3e18e10 commit 776ba27
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 23 deletions.
1 change: 1 addition & 0 deletions go-server/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
identity.key
21 changes: 5 additions & 16 deletions go-server/chatroom.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package main

import (
"context"
"encoding/json"

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

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

ctx context.Context
ps *pubsub.PubSub
Expand Down Expand Up @@ -60,6 +60,7 @@ func JoinChatRoom(ctx context.Context, ps *pubsub.PubSub, selfID peer.ID, nickna
nick: nickname,
roomName: roomName,
Messages: make(chan *ChatMessage, ChatRoomBufSize),
SysMessages: make(chan *ChatMessage, ChatRoomBufSize),
}

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

// Publish sends a message to the pubsub topic.
func (cr *ChatRoom) Publish(message string) error {
m := ChatMessage{
Message: message,
SenderID: cr.self.Pretty(),
SenderNick: cr.nick,
}
msgBytes, err := json.Marshal(m)
if err != nil {
return err
}
return cr.topic.Publish(cr.ctx, msgBytes)
return cr.topic.Publish(cr.ctx, []byte(message))
}

func (cr *ChatRoom) ListPeers() []peer.ID {
Expand All @@ -99,12 +91,9 @@ func (cr *ChatRoom) readLoop() {
}
cm := new(ChatMessage)
cm.Message = string(msg.Data)
cm.SenderID = string(msg.ID)
cm.SenderNick = "unknown"
cm.SenderID = msg.ID
cm.SenderNick = string(msg.ID[len(msg.ID)-8])

if err != nil {
continue
}
// send valid messages onto the Messages channel
cr.Messages <- cm
}
Expand Down
49 changes: 49 additions & 0 deletions go-server/identity.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package main
// Borrowed from https://github.com/libp2p/go-libp2p-relay-daemon/blob/master/identity.go

import (
"fmt"
"os"

"github.com/libp2p/go-libp2p/core/crypto"
)

// LoadIdentity reads a private key from the given path and, if it does not
// exist, generates a new one.
func LoadIdentity(idPath string) (crypto.PrivKey, error) {
if _, err := os.Stat(idPath); err == nil {
return ReadIdentity(idPath)
} else if os.IsNotExist(err) {
fmt.Printf("Generating peer identity in %s\n", idPath)
return GenerateIdentity(idPath)
} else {
return nil, err
}
}

// ReadIdentity reads a private key from the given path.
func ReadIdentity(path string) (crypto.PrivKey, error) {
bytes, err := os.ReadFile(path)
if err != nil {
return nil, err
}

return crypto.UnmarshalPrivateKey(bytes)
}

// GenerateIdentity writes a new random private key to the given path.
func GenerateIdentity(path string) (crypto.PrivKey, error) {
privk, _, err := crypto.GenerateKeyPair(crypto.Ed25519, 0)
if err != nil {
return nil, err
}

bytes, err := crypto.MarshalPrivateKey(privk)
if err != nil {
return nil, err
}

err = os.WriteFile(path, bytes, 0400)

return privk, err
}
38 changes: 33 additions & 5 deletions go-server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"context"
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"sync"
"time"
Expand All @@ -28,7 +30,7 @@ const DiscoveryInterval = time.Hour
// DiscoveryServiceTag is used in our mDNS advertisements to discover other chat peers.
const DiscoveryServiceTag = "universal-connectivity"

var ChatMsgChan chan *ChatMessage
var SysMsgChan chan *ChatMessage

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

discovery.Advertise(ctx, routingDiscovery, rendezvous)

ticker := time.NewTicker(time.Second * 1)
ticker := time.NewTicker(time.Second * 10)
defer ticker.Stop()

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

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

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

if *useLogger {
f, err := os.OpenFile("log.txt", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
log.Println("failed to open log file", err)
log.SetOutput(ioutil.Discard)
} else {
defer f.Close()
log.SetOutput(f)
}
} else {
log.SetOutput(ioutil.Discard)
}

ctx := context.Background()

// load our private key to generate the same peerID each time
privk, err := LoadIdentity(*idPath)
if err != nil {
panic(err)
}

// create a new libp2p Host that listens on a random TCP port
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"))
h, err := libp2p.New(
libp2p.Identity(privk),
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"),
)
if err != nil {
panic(err)
}
Expand All @@ -145,7 +173,7 @@ func main() {
if err != nil {
panic(err)
}
ChatMsgChan = cr.Messages
SysMsgChan = cr.SysMessages

// setup DHT with empty discovery peers
// so this will be a discovery peer for others
Expand Down
32 changes: 30 additions & 2 deletions go-server/ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"fmt"
"io"
"log"
"time"

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

msgW io.Writer
sysW io.Writer
inputCh chan string
doneCh chan struct{}
}
Expand All @@ -41,6 +43,19 @@ func NewChatUI(cr *ChatRoom) *ChatUI {
app.Draw()
})

// make a text view to contain our error messages
sysBox := tview.NewTextView()
sysBox.SetDynamicColors(true)
sysBox.SetBorder(true)
sysBox.SetTitle("System")

// text views are io.Writers, but they don't automatically refresh.
// this sets a change handler to force the app to redraw when we get
// new messages to display.
sysBox.SetChangedFunc(func() {
app.Draw()
})

// an input field for typing messages into
inputCh := make(chan string, 32)
input := tview.NewInputField().
Expand Down Expand Up @@ -87,8 +102,9 @@ func NewChatUI(cr *ChatRoom) *ChatUI {

flex := tview.NewFlex().
SetDirection(tview.FlexRow).
AddItem(chatPanel, 0, 1, false).
AddItem(input, 1, 1, true)
AddItem(chatPanel, 0, 3, false).
AddItem(sysBox, 0, 2, false).
AddItem(input, 2, 1, true)

app.SetRoot(flex, true)

Expand All @@ -97,6 +113,7 @@ func NewChatUI(cr *ChatRoom) *ChatUI {
app: app,
peersList: peersList,
msgW: msgBox,
sysW: sysBox,
inputCh: inputCh,
doneCh: make(chan struct{}, 1),
}
Expand Down Expand Up @@ -138,6 +155,13 @@ func (ui *ChatUI) displayChatMessage(cm *ChatMessage) {
fmt.Fprintf(ui.msgW, "%s %s\n", prompt, cm.Message)
}

// displayChatMessage writes a ChatMessage from the room to the message window,
// with the sender's nick highlighted in green.
func (ui *ChatUI) displaySysMessage(cm *ChatMessage) {
fmt.Fprintf(ui.sysW, "%s\n", cm.Message)
log.Println(cm.Message)
}

// displaySelfMessage writes a message from ourself to the message window,
// with our nick highlighted in yellow.
func (ui *ChatUI) displaySelfMessage(msg string) {
Expand Down Expand Up @@ -166,6 +190,10 @@ func (ui *ChatUI) handleEvents() {
// when we receive a message from the chat room, print it to the message window
ui.displayChatMessage(m)

case s := <-ui.cr.SysMessages:
// when we receive a message from the chat room, print it to the message window
ui.displaySysMessage(s)

case <-peerRefreshTicker.C:
// refresh the list of peers in the chat room periodically
ui.refreshPeers()
Expand Down

1 comment on commit 776ba27

@vercel
Copy link

@vercel vercel bot commented on 776ba27 Mar 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.