X-Git-Url: https://git.giacomolli.uk/onechat/blobdiff_plain/b2873a2a0bf8b0ba648aebac51759e5fed89a2e2..fb242538c1b012b0faf3cc4045a9b1c370f1ef42:/daemon.go?ds=inline diff --git a/daemon.go b/daemon.go index 68073d0..b37e0cf 100644 --- a/daemon.go +++ b/daemon.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "io" + "log" "net" "os" "path/filepath" @@ -51,9 +52,9 @@ type clientRequest struct { } // daemon runs "onechat daemon": it owns the root agent and its files and serves -// the clients on the Unix socket in the data directory until ctx ends. It holds -// the lock of the token file, so it is the only process that refreshes the -// tokens. +// the clients on the Unix socket in the data directory until ctx ends. When +// telegram.json exists, it also runs the Telegram bot. It holds the lock of the +// token file, so it is the only process that refreshes the tokens. func daemon(ctx context.Context, out io.Writer) (err error) { dir, err := dataDir() if err != nil { @@ -73,6 +74,11 @@ func daemon(ctx context.Context, out io.Writer) (err error) { return err } + botSettings, hasBot, err := loadBotConfig(dir) + if err != nil { + return err + } + tokens, err := lockTokens(dir) if err != nil { return err @@ -112,14 +118,24 @@ func daemon(ctx context.Context, out io.Writer) (err error) { return errors.Join(err, listener.Close()) } - return serveAgent(ctx, listener, func(notify func(Event)) (*loop, error) { + var telegram *telegramBot + if hasBot { + telegram = newTelegramBot(newBotAPI(telegramAPI, botSettings.Token), botSettings.Owner, filepath.Join(agent.dir, offsetFileName), log.New(out, "telegram: ", log.LstdFlags)) + _, err = fmt.Fprintln(out, "onechat: the Telegram bot waits for messages of its owner") + if err != nil { + return errors.Join(err, listener.Close()) + } + } + + return serveAgent(ctx, listener, telegram, func(notify func(Event)) (*loop, error) { return newLoop(agent, memory, client, time.Now, notify) }) } // serveAgent runs the loop that start makes and serves the clients of -// listener until ctx ends or the loop fails. It closes listener. -func serveAgent(ctx context.Context, listener net.Listener, start func(notify func(Event)) (*loop, error)) error { +// listener until ctx ends or the loop fails. It closes listener. When bot is +// not nil, it runs the bot too, and a failure of the bot stops the agent. +func serveAgent(ctx context.Context, listener net.Listener, bot *telegramBot, start func(notify func(Event)) (*loop, error)) error { ctx, cancel := context.WithCancel(ctx) defer cancel() @@ -127,7 +143,15 @@ func serveAgent(ctx context.Context, listener net.Listener, start func(notify fu clients: make(map[*hubClient]bool), } - l, err := start(hub.Broadcast) + notify := hub.Broadcast + if bot != nil { + notify = func(event Event) { + hub.Broadcast(event) + bot.Notify(event) + } + } + + l, err := start(notify) if err != nil { return errors.Join(err, listener.Close()) } @@ -140,17 +164,28 @@ func serveAgent(ctx context.Context, listener net.Listener, start func(notify fu hub.Accept(ctx, listener, l) }() + var botErr error + if bot != nil { + workers.Add(1) + go func() { + defer workers.Done() + + botErr = bot.Run(ctx, l) + cancel() + }() + } + err = l.Run(ctx) + if errors.Is(err, context.Canceled) { + err = nil + } + cancel() err = errors.Join(err, listener.Close()) hub.CloseAll() workers.Wait() - if errors.Is(err, context.Canceled) { - return nil - } - - return err + return errors.Join(err, botErr) } // hub holds the connected clients and sends each event to all of them.