-// serveAgent runs the loop that start makes and serves the clients of
-// 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()
-
- hub := &hub{
- clients: make(map[*hubClient]bool),
- }
-
- 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())
- }
-
- var workers sync.WaitGroup
- workers.Add(1)
- go func() {
- defer workers.Done()
-
- 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()
-
- return errors.Join(err, botErr)
-}
-
-// hub holds the connected clients and sends each event to all of them.
-type hub struct {