From c9a062ea049dff075afe3c8cfe938b458ae4d607 Mon Sep 17 00:00:00 2001 From: Lukasz Raczylo Date: Sun, 10 May 2026 21:51:13 +0100 Subject: [PATCH] feat(dispatch): expose Router.Process for non-Updater entry points Adds a public synchronous entry point that runs a single update through the global middleware chain and the dispatch table. Useful for callers that source updates outside the standard transport.Updater flow: custom webhook frameworks, message-bus consumers, and cross-library benchmarks driving the router without spinning up Run. Honours global middleware (Use); bypasses Run's concurrency semaphore since the caller controls parallelism. --- dispatch/router.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/dispatch/router.go b/dispatch/router.go index 7f82af5..4956102 100644 --- a/dispatch/router.go +++ b/dispatch/router.go @@ -300,6 +300,25 @@ func (r *Router) OnPurchasedPaidMedia(h Handler[*api.PaidMediaPurchased]) { // serial (legacy) behaviour. // // Run waits for all in-flight handlers to finish before returning. +// Process runs a single update through the router's middleware and handler +// chain synchronously. Entry point for callers sourcing updates outside the +// standard transport.Updater flow — custom webhook frameworks, message-bus +// consumers, or tests driving the router without spinning up Run. +// +// Honours the router's global middleware (Use) but bypasses the concurrency +// semaphore wired up by Run; the caller controls parallelism. +func (r *Router) Process(ctx context.Context, u *api.Update) error { + if u == nil { + return nil + } + root := r.dispatch + for i := len(r.globalMW) - 1; i >= 0; i-- { + root = r.globalMW[i](root) + } + c := NewContext(ctx, r.bot, u) + return root(c, u) +} + func (r *Router) Run(ctx context.Context, u transport.Updater) error { runErr := make(chan error, 1) go func() { runErr <- u.Run(ctx) }()