(new): v1.2 release
Golang lint / lint (push) Successful in 11m32s

This commit is contained in:
2026-08-19 14:58:25 +03:00
parent f03a081ed6
commit 29b208eeec
79 changed files with 7301 additions and 2060 deletions
+25 -6
View File
@@ -6,10 +6,17 @@ import (
"time"
)
// RunnerFn is the function type for a runner. It receives a pointer to
// the Bot and returns an error if execution fails.
// RunnerFn is the legacy function type for a runner. It receives a pointer to
// the Bot and returns an error if execution fails. New blocking or I/O work
// should use ContextRunnerFn so runtime cancellation can stop the callback.
//
// Subject to change in v2: runner callbacks may require context.Context.
type RunnerFn[T AppData] func(*Bot[T]) error
// ContextRunnerFn is a cancelable runner function.
// The runtime context is canceled when polling or webhook execution stops.
type ContextRunnerFn[T AppData] func(context.Context, *Bot[T]) error
// Runner represents a configurable background or one-time task to be
// executed by a Bot.
//
@@ -26,14 +33,18 @@ type Runner[T AppData] struct {
async bool // If true, runs in a goroutine; else, runs synchronously
every time.Duration // Interval between periodic executions; zero means one-shot
fn RunnerFn[T] // The function to execute
ctxFn ContextRunnerFn[T]
}
func executeRunner[T AppData](runner Runner[T], bot *Bot[T]) (err error) {
func executeRunnerWithContext[T AppData](ctx context.Context, runner Runner[T], bot *Bot[T]) (err error) {
defer func() {
if recovered := recover(); recovered != nil {
err = fmt.Errorf("runner %q panicked: %v", runner.name, recovered)
}
}()
if runner.ctxFn != nil {
return runner.ctxFn(ctx, bot)
}
if runner.fn == nil {
return fmt.Errorf("runner %q has no function", runner.name)
}
@@ -46,10 +57,18 @@ func executeRunner[T AppData](runner Runner[T], bot *Bot[T]) (err error) {
// goroutine that fires once when the bot runtime starts. Use Async and Every
// to customize this. Do not call builder methods concurrently or after the
// bot runtime has begun executing runners.
//
// Subject to change in v2: NewContextRunner may become the primary constructor.
func NewRunner[T AppData](name string, fn RunnerFn[T]) Runner[T] {
return Runner[T]{name: name, fn: fn, async: true, every: 0}
}
// NewContextRunner creates a runner whose callback observes runtime cancellation.
// It is the preferred constructor for I/O, blocking, and periodic work.
func NewContextRunner[T AppData](name string, fn ContextRunnerFn[T]) Runner[T] {
return Runner[T]{name: name, ctxFn: fn, async: true, every: 0}
}
// Async sets whether the runner executes synchronously or asynchronously.
// If true, the runner runs in a goroutine (non-blocking).
// If false, the runner blocks the caller during execution.
@@ -97,7 +116,7 @@ func (bot *Bot[T]) ExecRunners(ctx context.Context) {
go func(r Runner[T]) {
defer bot.runnerOnceWG.Done()
startedAt := time.Now()
err := executeRunner(r, bot)
err := executeRunnerWithContext(ctx, r, bot)
bot.safeEmitEvent(ctx, RunnerFinishedEvent{
Name: r.name,
Duration: time.Since(startedAt),
@@ -117,7 +136,7 @@ func (bot *Bot[T]) ExecRunners(ctx context.Context) {
} else if runner.every == 0 && !runner.async {
// One-time sync: block until done
t := time.Now()
err := executeRunner(runner, bot)
err := executeRunnerWithContext(ctx, runner, bot)
elapsed := time.Since(t)
bot.safeEmitEvent(ctx, RunnerFinishedEvent{
Name: runner.name,
@@ -158,7 +177,7 @@ func (bot *Bot[T]) ExecRunners(ctx context.Context) {
return
}
startedAt := time.Now()
err := executeRunner(r, bot)
err := executeRunnerWithContext(ctx, r, bot)
bot.safeEmitEvent(ctx, RunnerFinishedEvent{
Name: r.name,
Duration: time.Since(startedAt),