100 lines
3.7 KiB
Go
100 lines
3.7 KiB
Go
// Command echobot is the first bot of the unibots platform: a bot WITHOUT an
|
|
// LLM that demonstrates the two conversation patterns of the unibus bus.
|
|
//
|
|
// - Chat mode (bot<->human): the bot joins a cleartext room (room.ModeNATS)
|
|
// on a shared subject and echoes back every message it sees, prefixed with
|
|
// "echo: ". It never echoes its own messages (anti-loop guard), so two
|
|
// echobots on the same subject do not spin forever.
|
|
// - RPC mode (bot<->process): the bot registers a NATS request/reply
|
|
// responder on an rpc.* subject that returns "echo: " + the request body.
|
|
//
|
|
// echobot is application code that consumes the unibus client library; it is
|
|
// not a reusable registry function. The bus is the neighbouring `unibus` app.
|
|
package main
|
|
|
|
import (
|
|
"flag"
|
|
"log"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
|
|
"github.com/enmanuel/unibus/pkg/client"
|
|
"github.com/enmanuel/unibus/pkg/frame"
|
|
"github.com/enmanuel/unibus/pkg/room"
|
|
)
|
|
|
|
func main() {
|
|
var (
|
|
natsURL = flag.String("nats-url", "nats://127.0.0.1:4250", "NATS data-plane URL of the unibus bus")
|
|
ctrlURL = flag.String("ctrl-url", "http://127.0.0.1:8470", "membershipd control-plane HTTP URL")
|
|
roomSubject = flag.String("room-subject", "room.echo", "cleartext chat subject the bot listens on (bot<->human)")
|
|
rpcSubject = flag.String("rpc-subject", "rpc.echo", "request/reply subject the bot responds on (bot<->process)")
|
|
idFile = flag.String("id-file", "./local_files/echobot.id", "path to the bot's long-term identity file")
|
|
)
|
|
flag.Parse()
|
|
|
|
logger := log.New(os.Stderr, "[echobot] ", log.LstdFlags|log.Lmsgprefix)
|
|
|
|
id, err := client.LoadOrCreateIdentity(*idFile)
|
|
if err != nil {
|
|
logger.Fatalf("load/create identity %q: %v", *idFile, err)
|
|
}
|
|
|
|
c, err := client.New(*natsURL, *ctrlURL, id)
|
|
if err != nil {
|
|
logger.Fatalf("connect to bus (nats=%s ctrl=%s): %v", *natsURL, *ctrlURL, err)
|
|
}
|
|
defer c.Close()
|
|
|
|
self := c.Endpoint()
|
|
|
|
// --- Chat mode (bot<->human) --------------------------------------------
|
|
// A cleartext room mapped to the shared subject. NATS fans out by subject,
|
|
// so the bot shares the conversation with any peer on the same subject even
|
|
// if their room ids differ (same pattern as unibus worker/chat).
|
|
chatRoom, err := c.CreateRoom(*roomSubject, room.ModeNATS)
|
|
if err != nil {
|
|
logger.Fatalf("create chat room on subject %q: %v", *roomSubject, err)
|
|
}
|
|
|
|
chatSub, err := c.Subscribe(chatRoom, func(f frame.Frame, plaintext []byte) {
|
|
// Anti-loop guard: never echo our own messages, or two echobots (or a
|
|
// single bot seeing its own publish) would loop forever.
|
|
if f.Sender == self.ID {
|
|
return
|
|
}
|
|
reply := "echo: " + string(plaintext)
|
|
if err := c.Publish(chatRoom, []byte(reply)); err != nil {
|
|
logger.Printf("chat: publish echo failed: %v", err)
|
|
return
|
|
}
|
|
logger.Printf("chat: echoed %q -> %q (from %s)", string(plaintext), reply, f.Sender)
|
|
})
|
|
if err != nil {
|
|
logger.Fatalf("subscribe to chat room: %v", err)
|
|
}
|
|
defer chatSub.Unsubscribe()
|
|
|
|
// --- RPC mode (bot<->process) -------------------------------------------
|
|
// NATS request/reply: a responder on the rpc subject returns "echo: " + body.
|
|
rpcSub, err := c.Reply(*rpcSubject, func(body []byte) []byte {
|
|
reply := "echo: " + string(body)
|
|
logger.Printf("rpc: %q -> %q", string(body), reply)
|
|
return []byte(reply)
|
|
})
|
|
if err != nil {
|
|
logger.Fatalf("register rpc responder on %q: %v", *rpcSubject, err)
|
|
}
|
|
defer rpcSub.Unsubscribe()
|
|
|
|
logger.Printf("echobot up: endpoint=%s bus(nats=%s ctrl=%s) chat-subject=%q rpc-subject=%q",
|
|
self.ID, *natsURL, *ctrlURL, *roomSubject, *rpcSubject)
|
|
|
|
// --- Loop until SIGINT/SIGTERM, then shut down cleanly ------------------
|
|
sig := make(chan os.Signal, 1)
|
|
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
|
|
s := <-sig
|
|
logger.Printf("received %v, shutting down", s)
|
|
}
|