From 201848cbcdb9cb9a58c8bb164675491b14a0729e Mon Sep 17 00:00:00 2001 From: Nikolay Matyushin Date: Sat, 16 Aug 2025 22:28:42 +0400 Subject: [PATCH] feat: Graceful shutdown --- src/Funogram.Telegram/Bot.fs | 4 +++- src/Funogram/Tools.fs | 7 +++--- src/examples/Funogram.TestBot/Program.fs | 30 +++++++++++++++++------- 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/src/Funogram.Telegram/Bot.fs b/src/Funogram.Telegram/Bot.fs index 0e01fd9..33c81cd 100644 --- a/src/Funogram.Telegram/Bot.fs +++ b/src/Funogram.Telegram/Bot.fs @@ -106,6 +106,8 @@ let private runBot config me updateArrived updatesArrived = | None -> let rec loopAsync offset = async { + if Async.DefaultCancellationToken.IsCancellationRequested then + return () try let! updatesResult = Req.GetUpdates.Make(offset, ?limit = config.Limit, ?timeout = config.Timeout, ?allowedUpdates = (config.AllowedUpdates |> Option.map Seq.toArray)) @@ -113,7 +115,7 @@ let private runBot config me updateArrived updatesArrived = match updatesResult with | Ok updates when updates |> Seq.isEmpty |> not -> - let offset = updates |> Seq.map (fun f -> f.UpdateId) |> Seq.max |> fun x -> x + 1L + let offset = updates |> Seq.map (_.UpdateId) |> Seq.max |> fun x -> x + 1L processUpdates updates return! loopAsync offset // send new offset | Error e -> diff --git a/src/Funogram/Tools.fs b/src/Funogram/Tools.fs index 042700a..b928038 100644 --- a/src/Funogram/Tools.fs +++ b/src/Funogram/Tools.fs @@ -470,7 +470,6 @@ module Api = async { let client = config.Client let url = getUrl config request.MethodName - let serialize = multipartSerializers.GetOrAdd( request.GetType(), @@ -488,8 +487,8 @@ module Api = let mutable statusCode = -1 try let! result = - if hasData then client.PostAsync(url, content) |> Async.AwaitTask - else client.GetAsync(url) |> Async.AwaitTask + if hasData then client.PostAsync(url, content, cancellationToken = Async.DefaultCancellationToken) |> Async.AwaitTask + else client.GetAsync(url, cancellationToken = Async.DefaultCancellationToken) |> Async.AwaitTask statusCode <- result.StatusCode |> int @@ -518,7 +517,7 @@ module Api = try use content = new ByteArrayContent(bytes) content.Headers.ContentType <- MediaTypeHeaderValue.Parse("application/json") - let! result = client.PostAsync(url, content) |> Async.AwaitTask + let! result = client.PostAsync(url, content, cancellationToken = Async.DefaultCancellationToken) |> Async.AwaitTask statusCode <- result.StatusCode |> int use! stream = result.Content.ReadAsStreamAsync() |> Async.AwaitTask diff --git a/src/examples/Funogram.TestBot/Program.fs b/src/examples/Funogram.TestBot/Program.fs index e3da8a4..fafe0b6 100644 --- a/src/examples/Funogram.TestBot/Program.fs +++ b/src/examples/Funogram.TestBot/Program.fs @@ -1,6 +1,7 @@ module Funogram.TestBot.Program open System +open System.Threading open Funogram.TestBot open Funogram.Api open Funogram.Telegram @@ -18,12 +19,25 @@ type ConsoleLogger(color: ConsoleColor) = [] let main _ = - async { - let config = Config.defaultConfig |> Config.withReadTokenFromFile - let config = - { config with - RequestLogger = Some (ConsoleLogger(ConsoleColor.Green)) } - let! _ = Api.deleteWebhookBase () |> api config - return! startBot config Commands.Base.updateArrived None - } |> Async.RunSynchronously + Console.CancelKeyPress.Add(fun x -> + x.Cancel <- true + Async.CancelDefaultToken() + ) + + AppDomain.CurrentDomain.ProcessExit.Add(fun _ -> + Async.CancelDefaultToken() + ) + + try + async { + let config = Config.defaultConfig |> Config.withReadTokenFromFile + let config = + { config with + RequestLogger = Some (ConsoleLogger(ConsoleColor.Green)) } + let! _ = Api.deleteWebhookBase () |> api config + return! startBot config Commands.Base.updateArrived None + } |> Async.RunSynchronously + with + | :? OperationCanceledException -> + printfn "Graceful shutdown completed!" 0