diff --git a/.github/workflows/azure-deploy.yml b/.github/workflows/azure-deploy.yml new file mode 100644 index 0000000..05c0a99 --- /dev/null +++ b/.github/workflows/azure-deploy.yml @@ -0,0 +1,30 @@ +name: Deploy docker image + +on: + push: + branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - name: Docker Login + uses: Azure/docker-login@v1 + with: + username: ${{ secrets.AZURE_DOCKER_REGISTRY_USER }} + password: ${{ secrets.AZURE_DOCKER_REGISTRY_PASSWORD }} + login-server: https://dotnetru.azurecr.io/v1 + + - uses: actions/checkout@v2 + + - name: Build Docker image + run: docker build . -t grinder + + - name: Tag Docker image + run: docker tag grinder dotnetru.azurecr.io/vahter/grinder + + - name: Push Docker image + run: docker push dotnetru.azurecr.io/vahter/grinder + diff --git a/Dockerfile b/Dockerfile index 9bbcce5..e229f7c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,4 @@ -FROM resin/rpi-raspbian AS QEMU -# We need this container to copy QEMU files and use at ARM container -# This will fix pipeline crashes while doing smth - -FROM mcr.microsoft.com/dotnet/core/sdk:2.2.203-stretch AS build-dotnet +FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build-dotnet WORKDIR /app # Copy csproj and restore as distinct layers COPY *.sln ./ @@ -17,12 +13,12 @@ COPY src/Grinder/. ./src/Grinder COPY src/Grinder.Common/. ./src/Grinder.Common COPY src/Grinder.DataAccess/. ./src/Grinder.DataAccess WORKDIR /app/src/Grinder -RUN dotnet publish -r linux-arm -c Release -o out +RUN dotnet publish -r linux-x64 -c Release -o out # Build runtime image -FROM mcr.microsoft.com/dotnet/core/runtime:2.2.4-stretch-slim-arm32v7 -# After COPY we can RUN anything without crashes! -COPY --from=qemu /usr/bin/qemu-arm-static /usr/bin/qemu-arm-static +FROM mcr.microsoft.com/dotnet/core/runtime:3.1 + +EXPOSE 80 WORKDIR /app COPY --from=build-dotnet /app/src/Grinder/out . @@ -31,7 +27,5 @@ RUN mkdir -p /etc/grinder && mkdir -p /app/data VOLUME /etc/grinder/ VOLUME /app/data/ -# In final container we don't need this, removing -RUN rm -f /usr/bin/qemu-arm-static ENTRYPOINT ["dotnet", "Grinder.dll"] diff --git a/global.json b/global.json index b1df68f..d0bfd1d 100644 --- a/global.json +++ b/global.json @@ -1,5 +1,5 @@ { "sdk": { - "version": "2.2.100" + "version": "3.1.300" } } \ No newline at end of file diff --git a/src/Grinder.DataAccess/Grinder.DataAccess.csproj b/src/Grinder.DataAccess/Grinder.DataAccess.csproj index 68b9d7b..23e2b1d 100644 --- a/src/Grinder.DataAccess/Grinder.DataAccess.csproj +++ b/src/Grinder.DataAccess/Grinder.DataAccess.csproj @@ -1,7 +1,7 @@  - netcoreapp2.2 + netcoreapp3.1 true diff --git a/src/Grinder.ExportTool/Grinder.ExportTool.fsproj b/src/Grinder.ExportTool/Grinder.ExportTool.fsproj index 5a79879..6f1332d 100644 --- a/src/Grinder.ExportTool/Grinder.ExportTool.fsproj +++ b/src/Grinder.ExportTool/Grinder.ExportTool.fsproj @@ -2,7 +2,7 @@ Exe - netcoreapp2.2 + netcoreapp3.1 true true diff --git a/src/Grinder/Commands.fs b/src/Grinder/Commands.fs index 8162ea0..fca42c4 100644 --- a/src/Grinder/Commands.fs +++ b/src/Grinder/Commands.fs @@ -56,7 +56,9 @@ module Parser = | Ban of BanDuration | Unban - type Command = Command of Usernames * CommandAction + type Command = + | UserCommand of Usernames * CommandAction + | Ping let str s = pstring s @@ -107,18 +109,22 @@ module Parser = |> List.map attempt |> choice - let pban: Parser = + let pban: Parser = str "ban" .>> spaces >>. (pforeverBan <|> pdistinctTimeFractions) + |>> Ban let punban: Parser = str "unban" .>> spaces >>% Unban + let pping: Parser = + str "ping" >>% Ping + let pcommandAction: Parser = - (pban |>> Ban) <|> punban + pban <|> punban let parseCommand botUsername = pbotUsername botUsername >>. - pipe2 many1Usernames pcommandAction (fun usernames command -> Command(Usernames(usernames), command)) + (pping <|> pipe2 many1Usernames pcommandAction (fun usernames command -> UserCommand(Usernames(usernames), command))) let runCommandParser botUsername str: ParserResult = run (parseCommand botUsername) str @@ -275,12 +281,13 @@ module Processing = ChatId: TelegramChatId Usernames: UserUsername seq } - + type Command = | BanCommand of BanCommandContext | BanOnReplyCommand of ActionOnReplyCommandContext | UnbanCommand of UnbanCommandContext | UnbanOnReplyCommand of ActionOnReplyCommandContext + | PingCommand | DoNothingCommand type CommandError = @@ -374,7 +381,7 @@ module Processing = let parseTextMessage (context: TextMessageContext): Command = match Parser.parse %context.BotUsername context.MessageText with - | BotCommand(Command((Usernames usernames), Ban(duration))) -> + | BotCommand(UserCommand((Usernames usernames), Ban(duration))) -> let usernames = usernames |> Seq.map ^ fun username -> %username @@ -387,7 +394,7 @@ module Processing = Until = duration } BanCommand context - | BotCommand(Command((Usernames usernames), Unban)) -> + | BotCommand(UserCommand((Usernames usernames), Unban)) -> let usernames = usernames |> Seq.map ^ fun username -> %username @@ -399,6 +406,8 @@ module Processing = Usernames = usernames } UnbanCommand context + | BotCommand(Ping) -> + PingCommand | InvalidCommand _ -> DoNothingCommand @@ -538,6 +547,9 @@ module Processing = } return Some <| UnbanOnReplyMessage(context.From, message, errors) + | PingCommand -> + do! botApi.SendTextToChannel "pong" + return None | DoNothingCommand -> return None } diff --git a/src/Grinder/Grinder.fsproj b/src/Grinder/Grinder.fsproj index f507c82..795a112 100644 --- a/src/Grinder/Grinder.fsproj +++ b/src/Grinder/Grinder.fsproj @@ -2,7 +2,7 @@ Exe - netcoreapp2.2 + netcoreapp3.1 false true true @@ -15,6 +15,7 @@ + @@ -40,4 +41,4 @@ - + \ No newline at end of file diff --git a/src/Grinder/Program.fs b/src/Grinder/Program.fs index 0dd8b94..842a06c 100644 --- a/src/Grinder/Program.fs +++ b/src/Grinder/Program.fs @@ -1,5 +1,7 @@ namespace Grinder +open System.Net +open System.Threading open Microsoft.Extensions.Configuration open Funogram open Grinder @@ -10,6 +12,7 @@ open Funogram.Api open Funogram.Bot open Funogram.Types open FunogramExt +open System module Program = open System.Net.Http @@ -40,10 +43,11 @@ module Program = Bot: BotConfig } - let createHttpClient config = + let createHttpClient (config: Socks5Configuration) = let messageHandler = new HttpClientHandler() - messageHandler.Proxy <- HttpToSocks5Proxy(config.Hostname, config.Port, config.Username, config.Password) - messageHandler.UseProxy <- true + if not (isNull (box config)) then + messageHandler.Proxy <- HttpToSocks5Proxy(config.Hostname, config.Port, config.Username, config.Password) + messageHandler.UseProxy <- true new HttpClient(messageHandler) let createBotApi config (settings: BotSettings) = { @@ -124,11 +128,20 @@ module Program = [] let main _ = - let config = + let mutable configBuilder = ConfigurationBuilder() .AddJsonFile("appsettings.json", false, true) .AddJsonFile("/etc/grinder/appsettings.json", true, true) .AddEnvironmentVariables("Grinder_") + + match Environment.GetEnvironmentVariable("DOTNETRU_APP_CONFIG") with + | null -> () + | connString -> + configBuilder <- configBuilder + .AddAzureAppConfiguration connString + + let config = + configBuilder .Build() .Get() .Bot; @@ -142,23 +155,32 @@ module Program = GrinderContext.MigrateUp() - async { - printfn "Starting bot" - - let settings = { - Token = config.Token - ChatsToMonitor = ChatsToMonitor.Create config.ChatsToMonitor - AllowedUsers = AllowedUsers.Create config.AllowedUsers - ChannelId = %config.ChannelId - AdminUserId = %config.AdminUserId - } - do! startBot botConfiguration (onUpdate settings (createBotApi botConfiguration settings) dataApi) None - |> Async.StartChild - |> Async.Ignore - - printfn "Bot started" - do! Async.Sleep(-1) - } |> Async.RunSynchronously + printfn "Starting bot" + + let settings = { + Token = config.Token + ChatsToMonitor = ChatsToMonitor.Create config.ChatsToMonitor + AllowedUsers = AllowedUsers.Create config.AllowedUsers + ChannelId = %config.ChannelId + AdminUserId = %config.AdminUserId + } + startBot botConfiguration (onUpdate settings (createBotApi botConfiguration settings) dataApi) None + |> Async.Start + + printfn "Bot started" + + // Needed for azure web app deploy check. We have to response with anything on port 80 + use listener = new HttpListener() + listener.Prefixes.Add("http://*:80/") + listener.Start() + + let buffer = System.Text.Encoding.UTF8.GetBytes "OK" + + while true do + let ctx = listener.GetContext() + let output = ctx.Response.OutputStream + output.Write(buffer, 0, buffer.Length) + output.Close(); printfn "Bot exited" 0 // return an integer exit code \ No newline at end of file diff --git a/src/Grinder/appsettings.json b/src/Grinder/appsettings.json index f9c3834..2b88449 100644 --- a/src/Grinder/appsettings.json +++ b/src/Grinder/appsettings.json @@ -3,12 +3,13 @@ "Token": "REPLACEME", "ChannelId": 0, "AdminUserId": 0, - "Socks5Proxy": { - "Hostname": "REPLACEME", - "Port": 0, - "Username": "REPLACEME", - "Password": "REPLACEME" - }, +// uncomment if you need proxy +// "Socks5Proxy": { +// "Hostname": "REPLACEME", +// "Port": 0, +// "Username": "REPLACEME", +// "Password": "REPLACEME" +// }, "ChatsToMonitor": [ "REPLACEME" ], diff --git a/tests/Grinder.Tests/Grinder.Tests.fsproj b/tests/Grinder.Tests/Grinder.Tests.fsproj index 4d659f3..7a69813 100644 --- a/tests/Grinder.Tests/Grinder.Tests.fsproj +++ b/tests/Grinder.Tests/Grinder.Tests.fsproj @@ -2,7 +2,7 @@ Exe - netcoreapp2.2 + netcoreapp3.1 false false true