@@ -25,7 +25,7 @@ import (
2525func runTelegramCommand (controlDir string , paths ralph.Paths , args []string ) error {
2626 usage := func () {
2727 fmt .Fprintln (os .Stderr , "Usage: ralphctl --control-dir DIR --project-dir DIR telegram <run|setup|stop|status|tail> [flags]" )
28- fmt .Fprintln (os .Stderr , "Env: RALPH_TELEGRAM_BOT_TOKEN, RALPH_TELEGRAM_CHAT_IDS, RALPH_TELEGRAM_USER_IDS, RALPH_TELEGRAM_ALLOW_CONTROL, RALPH_TELEGRAM_NOTIFY, RALPH_TELEGRAM_NOTIFY_SCOPE" )
28+ fmt .Fprintln (os .Stderr , "Env: RALPH_TELEGRAM_BOT_TOKEN, RALPH_TELEGRAM_CHAT_IDS, RALPH_TELEGRAM_USER_IDS, RALPH_TELEGRAM_ALLOW_CONTROL, RALPH_TELEGRAM_NOTIFY, RALPH_TELEGRAM_NOTIFY_SCOPE, RALPH_TELEGRAM_COMMAND_TIMEOUT_SEC, RALPH_TELEGRAM_COMMAND_CONCURRENCY " )
2929 }
3030 if len (args ) == 0 {
3131 usage ()
@@ -68,6 +68,8 @@ func runTelegramRunCommand(controlDir string, paths ralph.Paths, args []string)
6868 notifyIntervalSec := fs .Int ("notify-interval-sec" , envIntDefault ("RALPH_TELEGRAM_NOTIFY_INTERVAL_SEC" , cfg .NotifyIntervalSec ), "status poll interval for notify alerts" )
6969 notifyRetryThreshold := fs .Int ("notify-retry-threshold" , envIntDefault ("RALPH_TELEGRAM_NOTIFY_RETRY_THRESHOLD" , cfg .NotifyRetryThreshold ), "codex retry alert threshold" )
7070 notifyPermStreakThreshold := fs .Int ("notify-perm-streak-threshold" , envIntDefault ("RALPH_TELEGRAM_NOTIFY_PERM_STREAK_THRESHOLD" , cfg .NotifyPermStreakThreshold ), "permission streak alert threshold" )
71+ commandTimeoutSec := fs .Int ("command-timeout-sec" , envIntDefault ("RALPH_TELEGRAM_COMMAND_TIMEOUT_SEC" , cfg .CommandTimeoutSec ), "timeout seconds per telegram command" )
72+ commandConcurrency := fs .Int ("command-concurrency" , envIntDefault ("RALPH_TELEGRAM_COMMAND_CONCURRENCY" , cfg .CommandConcurrency ), "max concurrent command workers across chats" )
7173 pollTimeoutSec := fs .Int ("poll-timeout-sec" , 30 , "telegram getUpdates timeout (seconds)" )
7274 offsetFile := fs .String ("offset-file" , filepath .Join (controlDir , "telegram.offset" ), "telegram update offset file" )
7375 if err := fs .Parse (args ); err != nil {
@@ -101,6 +103,12 @@ func runTelegramRunCommand(controlDir string, paths ralph.Paths, args []string)
101103 if * notifyIntervalSec <= 0 {
102104 return fmt .Errorf ("--notify-interval-sec must be > 0" )
103105 }
106+ if * commandTimeoutSec <= 0 {
107+ return fmt .Errorf ("--command-timeout-sec must be > 0" )
108+ }
109+ if * commandConcurrency <= 0 {
110+ return fmt .Errorf ("--command-concurrency must be > 0" )
111+ }
104112 resolvedNotifyScope , err := normalizeNotifyScope (* notifyScope )
105113 if err != nil {
106114 return fmt .Errorf ("invalid --notify-scope: %w" , err )
@@ -141,6 +149,8 @@ func runTelegramRunCommand(controlDir string, paths ralph.Paths, args []string)
141149 fmt .Printf ("Notify Every: %ds\n " , * notifyIntervalSec )
142150 fmt .Printf ("Retry Alert: %d\n " , * notifyRetryThreshold )
143151 fmt .Printf ("Perm Alert: %d\n " , * notifyPermStreakThreshold )
152+ fmt .Printf ("Cmd Timeout: %ds\n " , * commandTimeoutSec )
153+ fmt .Printf ("Cmd Workers: %d\n " , * commandConcurrency )
144154 fmt .Printf ("Allowed Chats: %d\n " , len (allowedChatIDs ))
145155 if len (allowedUserIDs ) > 0 {
146156 fmt .Printf ("Allowed Users: %d\n " , len (allowedUserIDs ))
@@ -157,15 +167,17 @@ func runTelegramRunCommand(controlDir string, paths ralph.Paths, args []string)
157167 ctx , stop := signal .NotifyContext (context .Background (), os .Interrupt , syscall .SIGTERM )
158168 defer stop ()
159169 return ralph .RunTelegramBot (ctx , ralph.TelegramBotOptions {
160- Token : * token ,
161- AllowedChatIDs : allowedChatIDs ,
162- AllowedUserIDs : allowedUserIDs ,
163- PollTimeoutSec : * pollTimeoutSec ,
164- NotifyIntervalSec : * notifyIntervalSec ,
165- OffsetFile : * offsetFile ,
166- Out : os .Stdout ,
167- OnCommand : telegramCommandHandler (controlDir , paths , * allowControl ),
168- OnNotifyTick : notifyHandler ,
170+ Token : * token ,
171+ AllowedChatIDs : allowedChatIDs ,
172+ AllowedUserIDs : allowedUserIDs ,
173+ PollTimeoutSec : * pollTimeoutSec ,
174+ NotifyIntervalSec : * notifyIntervalSec ,
175+ CommandTimeoutSec : * commandTimeoutSec ,
176+ CommandConcurrency : * commandConcurrency ,
177+ OffsetFile : * offsetFile ,
178+ Out : os .Stdout ,
179+ OnCommand : telegramCommandHandler (controlDir , paths , * allowControl ),
180+ OnNotifyTick : notifyHandler ,
169181 })
170182}
171183
@@ -243,6 +255,8 @@ func runTelegramSetupCommand(controlDir string, args []string) error {
243255 defaultNotifyInterval := envIntDefault ("RALPH_TELEGRAM_NOTIFY_INTERVAL_SEC" , cfg .NotifyIntervalSec )
244256 defaultNotifyRetry := envIntDefault ("RALPH_TELEGRAM_NOTIFY_RETRY_THRESHOLD" , cfg .NotifyRetryThreshold )
245257 defaultNotifyPerm := envIntDefault ("RALPH_TELEGRAM_NOTIFY_PERM_STREAK_THRESHOLD" , cfg .NotifyPermStreakThreshold )
258+ defaultCommandTimeout := envIntDefault ("RALPH_TELEGRAM_COMMAND_TIMEOUT_SEC" , cfg .CommandTimeoutSec )
259+ defaultCommandConcurrency := envIntDefault ("RALPH_TELEGRAM_COMMAND_CONCURRENCY" , cfg .CommandConcurrency )
246260
247261 fs := flag .NewFlagSet ("telegram setup" , flag .ContinueOnError )
248262 configFileFlag := fs .String ("config-file" , configFile , "telegram config file path" )
@@ -256,6 +270,8 @@ func runTelegramSetupCommand(controlDir string, args []string) error {
256270 notifyIntervalFlag := fs .Int ("notify-interval-sec" , defaultNotifyInterval , "notify interval seconds" )
257271 notifyRetryFlag := fs .Int ("notify-retry-threshold" , defaultNotifyRetry , "notify retry threshold" )
258272 notifyPermFlag := fs .Int ("notify-perm-streak-threshold" , defaultNotifyPerm , "notify permission streak threshold" )
273+ commandTimeoutFlag := fs .Int ("command-timeout-sec" , defaultCommandTimeout , "timeout seconds per telegram command" )
274+ commandConcurrencyFlag := fs .Int ("command-concurrency" , defaultCommandConcurrency , "max concurrent command workers across chats" )
259275 if err := fs .Parse (args ); err != nil {
260276 return err
261277 }
@@ -270,6 +286,8 @@ func runTelegramSetupCommand(controlDir string, args []string) error {
270286 NotifyIntervalSec : * notifyIntervalFlag ,
271287 NotifyRetryThreshold : * notifyRetryFlag ,
272288 NotifyPermStreakThreshold : * notifyPermFlag ,
289+ CommandTimeoutSec : * commandTimeoutFlag ,
290+ CommandConcurrency : * commandConcurrencyFlag ,
273291 }
274292 configFile = strings .TrimSpace (* configFileFlag )
275293
@@ -338,6 +356,22 @@ func runTelegramSetupCommand(controlDir string, args []string) error {
338356 if v , convErr := strconv .Atoi (strings .TrimSpace (permInput )); convErr == nil {
339357 final .NotifyPermStreakThreshold = v
340358 }
359+
360+ timeoutInput , err := promptFleetInput (reader , "Command timeout sec" , strconv .Itoa (final .CommandTimeoutSec ))
361+ if err != nil {
362+ return err
363+ }
364+ if v , convErr := strconv .Atoi (strings .TrimSpace (timeoutInput )); convErr == nil {
365+ final .CommandTimeoutSec = v
366+ }
367+
368+ workersInput , err := promptFleetInput (reader , "Command concurrency" , strconv .Itoa (final .CommandConcurrency ))
369+ if err != nil {
370+ return err
371+ }
372+ if v , convErr := strconv .Atoi (strings .TrimSpace (workersInput )); convErr == nil {
373+ final .CommandConcurrency = v
374+ }
341375 }
342376
343377 if strings .TrimSpace (final .Token ) == "" {
@@ -363,6 +397,12 @@ func runTelegramSetupCommand(controlDir string, args []string) error {
363397 if final .NotifyIntervalSec <= 0 {
364398 return fmt .Errorf ("notify-interval-sec must be > 0" )
365399 }
400+ if final .CommandTimeoutSec <= 0 {
401+ return fmt .Errorf ("command-timeout-sec must be > 0" )
402+ }
403+ if final .CommandConcurrency <= 0 {
404+ return fmt .Errorf ("command-concurrency must be > 0" )
405+ }
366406 scope , err := normalizeNotifyScope (final .NotifyScope )
367407 if err != nil {
368408 return fmt .Errorf ("notify-scope: %w" , err )
@@ -378,6 +418,8 @@ func runTelegramSetupCommand(controlDir string, args []string) error {
378418 fmt .Printf ("Allow Control: %t\n " , final .AllowControl )
379419 fmt .Printf ("Notify: %t\n " , final .Notify )
380420 fmt .Printf ("Notify Scope: %s\n " , final .NotifyScope )
421+ fmt .Printf ("Cmd Timeout: %ds\n " , final .CommandTimeoutSec )
422+ fmt .Printf ("Cmd Workers: %d\n " , final .CommandConcurrency )
381423 fmt .Println ()
382424 fmt .Println ("Next Commands" )
383425 fmt .Printf ("- run: ralphctl --project-dir \" $PWD\" telegram run --config-file %s\n " , configFile )
@@ -396,6 +438,8 @@ type telegramCLIConfig struct {
396438 NotifyIntervalSec int
397439 NotifyRetryThreshold int
398440 NotifyPermStreakThreshold int
441+ CommandTimeoutSec int
442+ CommandConcurrency int
399443}
400444
401445func defaultTelegramCLIConfig () telegramCLIConfig {
@@ -406,6 +450,8 @@ func defaultTelegramCLIConfig() telegramCLIConfig {
406450 NotifyIntervalSec : 30 ,
407451 NotifyRetryThreshold : 2 ,
408452 NotifyPermStreakThreshold : 3 ,
453+ CommandTimeoutSec : 300 ,
454+ CommandConcurrency : 4 ,
409455 }
410456}
411457
@@ -470,6 +516,12 @@ func loadTelegramCLIConfig(path string) (telegramCLIConfig, error) {
470516 if v , ok := parseIntRaw (values ["RALPH_TELEGRAM_NOTIFY_PERM_STREAK_THRESHOLD" ]); ok {
471517 cfg .NotifyPermStreakThreshold = v
472518 }
519+ if v , ok := parseIntRaw (values ["RALPH_TELEGRAM_COMMAND_TIMEOUT_SEC" ]); ok {
520+ cfg .CommandTimeoutSec = v
521+ }
522+ if v , ok := parseIntRaw (values ["RALPH_TELEGRAM_COMMAND_CONCURRENCY" ]); ok {
523+ cfg .CommandConcurrency = v
524+ }
473525 return cfg , nil
474526}
475527
@@ -492,6 +544,8 @@ func saveTelegramCLIConfig(path string, cfg telegramCLIConfig) error {
492544 b .WriteString ("RALPH_TELEGRAM_NOTIFY_INTERVAL_SEC=" + strconv .Itoa (cfg .NotifyIntervalSec ) + "\n " )
493545 b .WriteString ("RALPH_TELEGRAM_NOTIFY_RETRY_THRESHOLD=" + strconv .Itoa (cfg .NotifyRetryThreshold ) + "\n " )
494546 b .WriteString ("RALPH_TELEGRAM_NOTIFY_PERM_STREAK_THRESHOLD=" + strconv .Itoa (cfg .NotifyPermStreakThreshold ) + "\n " )
547+ b .WriteString ("RALPH_TELEGRAM_COMMAND_TIMEOUT_SEC=" + strconv .Itoa (cfg .CommandTimeoutSec ) + "\n " )
548+ b .WriteString ("RALPH_TELEGRAM_COMMAND_CONCURRENCY=" + strconv .Itoa (cfg .CommandConcurrency ) + "\n " )
495549 if err := os .WriteFile (path , []byte (b .String ()), 0o600 ); err != nil {
496550 return err
497551 }
0 commit comments