diff --git a/Dockerfile b/Dockerfile index ba7cd73..33c8c9b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/dotnet/sdk:7.0.402-jammy as build-env +FROM mcr.microsoft.com/dotnet/sdk:8.0.302-jammy as build-env ### workaround for testcontainers resource reaper issue ARG RESOURCE_REAPER_SESSION_ID="00000000-0000-0000-0000-000000000000" @@ -12,7 +12,7 @@ COPY src/VahterBanBot . COPY global.json . RUN dotnet publish -c Release -o /publish -FROM mcr.microsoft.com/dotnet/aspnet:7.0 as runtime +FROM mcr.microsoft.com/dotnet/aspnet:8.0 as runtime WORKDIR /publish COPY --from=build-env /publish . ENTRYPOINT ["dotnet", "VahterBanBot.dll"] diff --git a/global.json b/global.json index ad69c0c..2434529 100644 --- a/global.json +++ b/global.json @@ -1,5 +1,5 @@ { "sdk": { - "version": "7.0.402" + "version": "8.0.302" } } \ No newline at end of file diff --git a/src/VahterBanBot.Tests/ContainerTestBase.fs b/src/VahterBanBot.Tests/ContainerTestBase.fs index ff00d65..7e57974 100644 --- a/src/VahterBanBot.Tests/ContainerTestBase.fs +++ b/src/VahterBanBot.Tests/ContainerTestBase.fs @@ -82,6 +82,10 @@ type VahterTestContainers() = .WithEnvironment("IGNORE_SIDE_EFFECTS", "false") .WithEnvironment("USE_POLLING", "false") .WithEnvironment("DATABASE_URL", internalConnectionString) + // .net 8.0 upgrade has a breaking change + // https://learn.microsoft.com/en-us/dotnet/core/compatibility/containers/8.0/aspnet-port + // Azure default port for containers is 80, se we need explicitly set it + .WithEnvironment("ASPNETCORE_HTTP_PORTS", "80") .DependsOn(flywayContainer) .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(80)) .Build() diff --git a/src/VahterBanBot.Tests/VahterBanBot.Tests.fsproj b/src/VahterBanBot.Tests/VahterBanBot.Tests.fsproj index 1d20868..84eb0f0 100644 --- a/src/VahterBanBot.Tests/VahterBanBot.Tests.fsproj +++ b/src/VahterBanBot.Tests/VahterBanBot.Tests.fsproj @@ -1,7 +1,7 @@ - net7.0 + net8.0 false false true diff --git a/src/VahterBanBot/Antispam.fs b/src/VahterBanBot/Antispam.fs index 8c4bad0..52fad42 100644 --- a/src/VahterBanBot/Antispam.fs +++ b/src/VahterBanBot/Antispam.fs @@ -34,8 +34,9 @@ let phrases = [ let countPhrases (wl: string list) = // premium performance - let rec countPhrase wl totalScore phrase = - let score, p::ps = phrase + let rec countPhrase wl totalScore (score, psx as phrase) = + // List.tail should be safe here as we are passing list of phrases above which is always non-empty + let p, ps = List.head psx, List.tail psx match wl with | w :: ws when w = p -> diff --git a/src/VahterBanBot/Bot.fs b/src/VahterBanBot/Bot.fs index 8b67ce5..be22e01 100644 --- a/src/VahterBanBot/Bot.fs +++ b/src/VahterBanBot/Bot.fs @@ -402,6 +402,104 @@ let warnSpamDetection logger.LogInformation logMsg } +let justMessage + (botClient: ITelegramBotClient) + (botConfig: BotConfiguration) + (logger: ILogger) + (message: Message) = task { + let spamScore = if message.Text <> null then calcSpamScore message.Text else 0 + + if spamScore >= 100 then + do! warnSpamDetection botClient botConfig message logger spamScore + + use _ = + botActivity + .StartActivity("justMessage") + .SetTag("fromUserId", message.From.Id) + .SetTag("fromUsername", message.From.Username) + .SetTag("spamScore", spamScore) + do! + message + |> DbMessage.newMessage + |> DB.insertMessage + |> taskIgnore +} + +let adminCommand + (botClient: ITelegramBotClient) + (botConfig: BotConfiguration) + (logger: ILogger) + (message: Message) = + + // aux functions to overcome annoying FS3511: This state machine is not statically compilable. + let banOnReplyAux() = task { + let targetUserId = message.ReplyToMessage.From.Id + let targetUsername = Option.ofObj message.ReplyToMessage.From.Username + let authed = + isBanAuthorized + botConfig + message + logger + targetUserId + targetUsername + true + if authed then + do! banOnReply botClient botConfig message logger + } + let unbanAux() = task { + let targetUserId = message.Text.Split(" ", StringSplitOptions.RemoveEmptyEntries)[1] |> int64 + let authed = + isBanAuthorized + botConfig + message + logger + targetUserId + None + false + if authed then + do! unban botClient botConfig message logger targetUserId + } + let softBanOnReplyAux() = task { + let targetUserId = message.ReplyToMessage.From.Id + let targetUsername = Option.ofObj message.ReplyToMessage.From.Username + let authed = + isBanAuthorized + botConfig + message + logger + targetUserId + targetUsername + true + if authed then + do! softBanOnReply botClient botConfig message logger + } + + task { + use _ = botActivity.StartActivity("adminCommand") + // delete command message + let deleteCmdTask = task { + use _ = + botActivity + .StartActivity("deleteCmdMsg") + .SetTag("msgId", message.MessageId) + .SetTag("chatId", message.Chat.Id) + .SetTag("chatUsername", message.Chat.Username) + do! botClient.DeleteMessageAsync(ChatId(message.Chat.Id), message.MessageId) + |> safeTaskAwait (fun e -> logger.LogError ($"Failed to delete ping message {message.MessageId} from chat {message.Chat.Id}", e)) + } + // check that user is allowed to (un)ban others + if isBanOnReplyCommand message then + do! banOnReplyAux() + elif isUnbanCommand message then + do! unbanAux() + elif isSoftBanOnReplyCommand message then + do! softBanOnReplyAux() + // ping command for testing that bot works and you can talk to it + elif isPingCommand message then + do! ping botClient message + do! deleteCmdTask + } + let onUpdate (botClient: ITelegramBotClient) (botConfig: BotConfiguration) @@ -435,78 +533,9 @@ let onUpdate // check if message is a known command from authorized user elif isKnownCommand message && isMessageFromAdmin botConfig message then - use _ = botActivity.StartActivity("adminCommand") - // delete command message - let deleteCmdTask = task { - use _ = - botActivity - .StartActivity("deleteCmdMsg") - .SetTag("msgId", message.MessageId) - .SetTag("chatId", message.Chat.Id) - .SetTag("chatUsername", message.Chat.Username) - do! botClient.DeleteMessageAsync(ChatId(message.Chat.Id), message.MessageId) - |> safeTaskAwait (fun e -> logger.LogError ($"Failed to delete ping message {message.MessageId} from chat {message.Chat.Id}", e)) - } - // check that user is allowed to (un)ban others - if isBanOnReplyCommand message then - let targetUserId = message.ReplyToMessage.From.Id - let targetUsername = Option.ofObj message.ReplyToMessage.From.Username - let authed = - isBanAuthorized - botConfig - message - logger - targetUserId - targetUsername - true - if authed then - do! banOnReply botClient botConfig message logger - elif isUnbanCommand message then - let targetUserId = message.Text.Split(" ", StringSplitOptions.RemoveEmptyEntries)[1] |> int64 - let authed = - isBanAuthorized - botConfig - message - logger - targetUserId - None - false - if authed then - do! unban botClient botConfig message logger targetUserId - elif isSoftBanOnReplyCommand message then - let targetUserId = message.ReplyToMessage.From.Id - let targetUsername = Option.ofObj message.ReplyToMessage.From.Username - let authed = - isBanAuthorized - botConfig - message - logger - targetUserId - targetUsername - true - if authed then - do! softBanOnReply botClient botConfig message logger - // ping command for testing that bot works and you can talk to it - elif isPingCommand message then - do! ping botClient message - do! deleteCmdTask + do! adminCommand botClient botConfig logger message // if message is not a command from authorized user, just save it ID to DB else - let spamScore = if message.Text <> null then calcSpamScore message.Text else 0 - - if spamScore >= 100 then - do! warnSpamDetection botClient botConfig message logger spamScore - - use _ = - botActivity - .StartActivity("justMessage") - .SetTag("fromUserId", message.From.Id) - .SetTag("fromUsername", message.From.Username) - .SetTag("spamScore", spamScore) - do! - message - |> DbMessage.newMessage - |> DB.insertMessage - |> taskIgnore + do! justMessage botClient botConfig logger message } diff --git a/src/VahterBanBot/Program.fs b/src/VahterBanBot/Program.fs index a7880bb..2c288c4 100644 --- a/src/VahterBanBot/Program.fs +++ b/src/VahterBanBot/Program.fs @@ -1,4 +1,6 @@ -open System +#nowarn "44" // open telemetry is going crazy with warnings + +open System open System.Collections.Generic open System.Threading open System.Threading.Tasks diff --git a/src/VahterBanBot/VahterBanBot.fsproj b/src/VahterBanBot/VahterBanBot.fsproj index 2e681da..bc5de2b 100644 --- a/src/VahterBanBot/VahterBanBot.fsproj +++ b/src/VahterBanBot/VahterBanBot.fsproj @@ -2,8 +2,8 @@ Exe - net7.0 - true + net8.0 + true @@ -18,21 +18,21 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + +