2023-08-21 03:50:38 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"flag"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/libp2p/go-libp2p"
|
|
|
|
pubsub "github.com/libp2p/go-libp2p-pubsub"
|
|
|
|
"github.com/libp2p/go-libp2p/core/host"
|
|
|
|
"github.com/libp2p/go-libp2p/core/peer"
|
|
|
|
"github.com/libp2p/go-libp2p/p2p/discovery/mdns"
|
|
|
|
)
|
|
|
|
|
|
|
|
// DiscoveryInterval is how often we re-publish our mDNS records.
|
|
|
|
const DiscoveryInterval = time.Hour
|
|
|
|
|
|
|
|
// DiscoveryServiceTag is used in our mDNS advertisements to discover other chat peers.
|
|
|
|
const DiscoveryServiceTag = "pubsub-chat-example"
|
|
|
|
|
|
|
|
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", "awesome-chat-room", "name of chat room to join")
|
|
|
|
flag.Parse()
|
|
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
|
// create a new libp2p Host that listens on a random TCP port
|
|
|
|
h, err := libp2p.New(libp2p.ListenAddrStrings("/ip4/0.0.0.0/tcp/0"))
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// create a new PubSub service using the GossipSub router
|
|
|
|
ps, err := pubsub.NewGossipSub(ctx, h)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// setup local mDNS discovery
|
|
|
|
if err := setupDiscovery(h); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// use the nickname from the cli flag, or a default if blank
|
|
|
|
nick := *nickFlag
|
|
|
|
if len(nick) == 0 {
|
|
|
|
nick = defaultNick(h.ID())
|
|
|
|
}
|
|
|
|
|
|
|
|
// join the room from the cli flag, or the flag default
|
|
|
|
room := *roomFlag
|
|
|
|
|
|
|
|
// join the chat room
|
|
|
|
cr, err := JoinChatRoom(ctx, ps, h.ID(), nick, room)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// draw the UI
|
|
|
|
ui := NewChatUI(cr)
|
|
|
|
if err = ui.Run(); err != nil {
|
|
|
|
printErr("error running text UI: %s", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// printErr is like fmt.Printf, but writes to stderr.
|
|
|
|
func printErr(m string, args ...interface{}) {
|
|
|
|
fmt.Fprintf(os.Stderr, m, args...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// defaultNick generates a nickname based on the $USER environment variable and
|
|
|
|
// the last 8 chars of a peer ID.
|
|
|
|
func defaultNick(p peer.ID) string {
|
|
|
|
return fmt.Sprintf("%s-%s", os.Getenv("USER"), shortID(p))
|
|
|
|
}
|
|
|
|
|
|
|
|
// shortID returns the last 8 chars of a base58-encoded peer id.
|
|
|
|
func shortID(p peer.ID) string {
|
2024-06-08 11:32:45 +00:00
|
|
|
pretty := p.String()
|
2023-08-21 03:50:38 +00:00
|
|
|
return pretty[len(pretty)-8:]
|
|
|
|
}
|
|
|
|
|
|
|
|
// discoveryNotifee gets notified when we find a new peer via mDNS discovery
|
|
|
|
type discoveryNotifee struct {
|
|
|
|
h host.Host
|
|
|
|
}
|
|
|
|
|
|
|
|
// HandlePeerFound connects to peers discovered via mDNS. Once they're connected,
|
|
|
|
// the PubSub system will automatically start interacting with them if they also
|
|
|
|
// support PubSub.
|
|
|
|
func (n *discoveryNotifee) HandlePeerFound(pi peer.AddrInfo) {
|
2024-06-08 11:32:45 +00:00
|
|
|
fmt.Printf("discovered new peer %s\n", pi.ID)
|
2023-08-21 03:50:38 +00:00
|
|
|
err := n.h.Connect(context.Background(), pi)
|
|
|
|
if err != nil {
|
2024-06-08 11:32:45 +00:00
|
|
|
fmt.Printf("error connecting to peer %s: %s\n", pi.ID, err)
|
2023-08-21 03:50:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// setupDiscovery creates an mDNS discovery service and attaches it to the libp2p Host.
|
|
|
|
// This lets us automatically discover peers on the same LAN and connect to them.
|
|
|
|
func setupDiscovery(h host.Host) error {
|
|
|
|
// setup mDNS discovery to find local peers
|
|
|
|
s := mdns.NewMdnsService(h, DiscoveryServiceTag, &discoveryNotifee{h: h})
|
|
|
|
return s.Start()
|
|
|
|
}
|