feat(config): отключаемый Telegram-канал (telegram.enabled)
Some checks failed
CI / test (push) Failing after 1m20s
CI / build-and-package (amd64, linux) (push) Failing after 1m5s
CI / build-and-package (amd64, windows) (push) Successful in 31s

- TelegramCfg.Enabled (дефолт true); при false канал не создаётся и не крепится в Router, long-poll не стартует — нет сетевых вызовов к api.telegram.org
- Validate требует token/chat_id только при enabled
- Load пресетит enabled=true до unmarshal (applyDefaults для bool не различает явный false)
- тесты, README и config.yaml.example обновлены
This commit is contained in:
ki.sagidullin
2026-08-23 17:36:05 +05:00
parent 4c0dc80aa8
commit d731a7429d
7 changed files with 95 additions and 33 deletions

View File

@@ -63,7 +63,7 @@ type App struct {
CoreCtx *core.Core
Worker *worker.Worker
Updater *update.Updater
tg *telegram.Channel // сохранена для Run
tg *telegram.Channel // сохранена для Run (nil, если telegram.enabled=false)
pool *opencode.Pool // пул opencode serve-серверов (API-режим)
// Events — доменная шина UI; LogEvents — шина логов (панель «Логи»).
@@ -224,12 +224,14 @@ func New(configPath, version, updateToken string, noUI bool) (*App, error) {
}
}
// Telegram-канал
tg := telegram.New(cfg.Telegram.Token, cfg.Chat.PollInterval.Duration())
a.tg = tg
if err := router.Attach(tg); err != nil {
store.Close()
return nil, fmt.Errorf("attach telegram: %w", err)
// Telegram-канал (если включён; иначе сетевые вызовы к Telegram не идут)
if cfg.Telegram.Enabled {
tg := telegram.New(cfg.Telegram.Token, cfg.Chat.PollInterval.Duration())
a.tg = tg
if err := router.Attach(tg); err != nil {
store.Close()
return nil, fmt.Errorf("attach telegram: %w", err)
}
}
// Worker — polling-планировщик dev-агента
@@ -291,15 +293,15 @@ func (a *App) Run(ctx context.Context) error {
}
defer a.pool.Close()
// Канал для проверки Telegram-ошибки (горутина оборачивает Run)
// Telegram: long-poll цикл в горутине (только если канал включён)
tgErr := make(chan error, 1)
// Telegram: long-poll цикл в горутине
go func() {
tg := a.telegramChannel()
log.Print("app: telegram poll started")
tgErr <- tg.Run(ctx)
}()
if a.telegramChannel() != nil {
go func() {
tg := a.telegramChannel()
log.Print("app: telegram poll started")
tgErr <- tg.Run(ctx)
}()
}
// Worker: poll-цикл (неблокирующий — стартует свою горутину)
a.Worker.Start(ctx)
@@ -320,6 +322,23 @@ func (a *App) Run(ctx context.Context) error {
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
if a.telegramChannel() != nil {
select {
case <-ctx.Done():
log.Print("app: context cancelled")
return nil
case sig := <-sigCh:
log.Printf("app: signal %s — shutting down", sig)
cancel()
return nil
case err := <-tgErr:
if err != nil && !errors.Is(err, context.Canceled) {
return fmt.Errorf("%w: %v", ErrChannelFatal, err)
}
return nil
}
}
select {
case <-ctx.Done():
log.Print("app: context cancelled")
@@ -328,11 +347,6 @@ func (a *App) Run(ctx context.Context) error {
log.Printf("app: signal %s — shutting down", sig)
cancel()
return nil
case err := <-tgErr:
if err != nil && !errors.Is(err, context.Canceled) {
return fmt.Errorf("%w: %v", ErrChannelFatal, err)
}
return nil
}
}