From c9d0b478cded017cf83305039d9c9ea3eb251376 Mon Sep 17 00:00:00 2001 From: boogiedk Date: Sat, 14 Oct 2023 16:30:05 +0300 Subject: [PATCH 01/35] upgrade workflow --- .github/workflows/docker-deploy.yml | 41 ++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/.github/workflows/docker-deploy.yml b/.github/workflows/docker-deploy.yml index f175650..977295d 100644 --- a/.github/workflows/docker-deploy.yml +++ b/.github/workflows/docker-deploy.yml @@ -1,4 +1,4 @@ -name: Docker Deploy +name: CI/CD Pipline on: pull_request: @@ -12,13 +12,40 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v2 + + - name: Login to Docker Hub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} - name: Build and push Docker image + uses: docker/build-push-action@v2 + with: + context: . + push: true + tags: ${{ secrets.DOCKER_USERNAME }}/serf-bot:latest + + - name: Install Expect + run: sudo apt-get install -y expect + + - name: Deploy to VPS env: - DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} - DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} + VPS_HOST: ${{ secrets.VPS_HOST }} + VPS_USER: ${{ secrets.VPS_USERNAME }} + VPS_SSH_PRIVATE_KEY: ${{ secrets.VPS_SSH_PRIVATE_KEY }} + VPS_SSH_PRIVATE_KEY_PASSPHRASE: ${{ secrets.VPS_SSH_PRIVATE_KEY_PASSPHRASE }} + APPLICATION_NAME: serf-bot run: | - docker build -t serf-bot-image . - docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD - docker tag serf-bot-image boogiedk/serf-bot-image:latest - docker push boogiedk/serf-bot-image:latest + echo "${{ env.VPS_SSH_PRIVATE_KEY }}" > private_key + chmod 600 private_key + eval "$(ssh-agent -s)" + echo "${{ env.VPS_SSH_PRIVATE_KEY_PASSPHRASE }}" | expect -c "spawn ssh-add private_key; expect \"Enter passphrase:\"; send -- \"${{ env.VPS_SSH_PRIVATE_KEY_PASSPHRASE }}\r\"; expect eof" + rm -f private_key + ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${{ env.VPS_USER }}@${{ env.VPS_HOST }} <<-EOF + docker login -u ${{ secrets.DOCKER_HUB_USERNAME }} -p ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} + docker pull ${{ secrets.DOCKER_HUB_USERNAME }}/${{ env.APPLICATION_NAME }}:latest + docker stop ${{ env.APPLICATION_NAME }} || true + docker rm ${{ env.APPLICATION_NAME }} || true + docker run -d --name ${{ env.APPLICATION_NAME }} -p 3000:3000 ${{ secrets.DOCKER_HUB_USERNAME }}/${{ env.APPLICATION_NAME }}:latest + EOF From 81963377ad6059e49b315531b24c3d019fd6b115 Mon Sep 17 00:00:00 2001 From: boogiedk Date: Sat, 14 Oct 2023 16:39:15 +0300 Subject: [PATCH 02/35] fix --- .github/workflows/docker-deploy.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/docker-deploy.yml b/.github/workflows/docker-deploy.yml index 977295d..60cdb35 100644 --- a/.github/workflows/docker-deploy.yml +++ b/.github/workflows/docker-deploy.yml @@ -44,8 +44,8 @@ jobs: rm -f private_key ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${{ env.VPS_USER }}@${{ env.VPS_HOST }} <<-EOF docker login -u ${{ secrets.DOCKER_HUB_USERNAME }} -p ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} - docker pull ${{ secrets.DOCKER_HUB_USERNAME }}/${{ env.APPLICATION_NAME }}:latest - docker stop ${{ env.APPLICATION_NAME }} || true - docker rm ${{ env.APPLICATION_NAME }} || true - docker run -d --name ${{ env.APPLICATION_NAME }} -p 3000:3000 ${{ secrets.DOCKER_HUB_USERNAME }}/${{ env.APPLICATION_NAME }}:latest + docker pull ${{ secrets.DOCKER_HUB_USERNAME }}/serf-bot:latest + docker stop serf-bot || true + docker rm serf-bot || true + docker run -d --name serf-bot -p 3000:3000 ${{ secrets.DOCKER_HUB_USERNAME }}/serf-bot:latest EOF From 93efb6c3a487a5cf6976b678d865265b3b612a80 Mon Sep 17 00:00:00 2001 From: boogiedk Date: Sat, 14 Oct 2023 16:42:27 +0300 Subject: [PATCH 03/35] fix --- .github/workflows/docker-deploy.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/docker-deploy.yml b/.github/workflows/docker-deploy.yml index 60cdb35..fd55c12 100644 --- a/.github/workflows/docker-deploy.yml +++ b/.github/workflows/docker-deploy.yml @@ -35,7 +35,6 @@ jobs: VPS_USER: ${{ secrets.VPS_USERNAME }} VPS_SSH_PRIVATE_KEY: ${{ secrets.VPS_SSH_PRIVATE_KEY }} VPS_SSH_PRIVATE_KEY_PASSPHRASE: ${{ secrets.VPS_SSH_PRIVATE_KEY_PASSPHRASE }} - APPLICATION_NAME: serf-bot run: | echo "${{ env.VPS_SSH_PRIVATE_KEY }}" > private_key chmod 600 private_key From 65201e2b19349d9939b7081f801b861f5107a2c1 Mon Sep 17 00:00:00 2001 From: boogiedk Date: Sat, 14 Oct 2023 16:54:28 +0300 Subject: [PATCH 04/35] fix --- .github/workflows/docker-deploy.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docker-deploy.yml b/.github/workflows/docker-deploy.yml index fd55c12..96cb123 100644 --- a/.github/workflows/docker-deploy.yml +++ b/.github/workflows/docker-deploy.yml @@ -42,9 +42,9 @@ jobs: echo "${{ env.VPS_SSH_PRIVATE_KEY_PASSPHRASE }}" | expect -c "spawn ssh-add private_key; expect \"Enter passphrase:\"; send -- \"${{ env.VPS_SSH_PRIVATE_KEY_PASSPHRASE }}\r\"; expect eof" rm -f private_key ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${{ env.VPS_USER }}@${{ env.VPS_HOST }} <<-EOF - docker login -u ${{ secrets.DOCKER_HUB_USERNAME }} -p ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} - docker pull ${{ secrets.DOCKER_HUB_USERNAME }}/serf-bot:latest + docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} + docker pull ${{ secrets.DOCKER_USERNAME }}/serf-bot:latest docker stop serf-bot || true docker rm serf-bot || true - docker run -d --name serf-bot -p 3000:3000 ${{ secrets.DOCKER_HUB_USERNAME }}/serf-bot:latest + docker run -d --name serf-bot -p 3000:3000 ${{ secrets.DOCKER_USERNAME }}/serf-bot:latest EOF From ee2825754108bb45f702ae1a533dac76611663a9 Mon Sep 17 00:00:00 2001 From: boogiedk Date: Sat, 14 Oct 2023 16:57:09 +0300 Subject: [PATCH 05/35] fix port --- .github/workflows/docker-deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-deploy.yml b/.github/workflows/docker-deploy.yml index 96cb123..48b28e8 100644 --- a/.github/workflows/docker-deploy.yml +++ b/.github/workflows/docker-deploy.yml @@ -46,5 +46,5 @@ jobs: docker pull ${{ secrets.DOCKER_USERNAME }}/serf-bot:latest docker stop serf-bot || true docker rm serf-bot || true - docker run -d --name serf-bot -p 3000:3000 ${{ secrets.DOCKER_USERNAME }}/serf-bot:latest + docker run -d --name serf-bot -p 3005:3005 ${{ secrets.DOCKER_USERNAME }}/serf-bot:latest EOF From d14454c35694974ee8c2d3d66531eb6bba59a11e Mon Sep 17 00:00:00 2001 From: boogiedk Date: Sat, 14 Oct 2023 19:44:24 +0300 Subject: [PATCH 06/35] refactor --- SerfBot/OpenAiApi.fs | 4 ++-- SerfBot/TelegramBot.fs | 32 ++++++++++++-------------------- 2 files changed, 14 insertions(+), 22 deletions(-) diff --git a/SerfBot/OpenAiApi.fs b/SerfBot/OpenAiApi.fs index ca70766..c1d7803 100644 --- a/SerfBot/OpenAiApi.fs +++ b/SerfBot/OpenAiApi.fs @@ -4,7 +4,7 @@ open OpenAI_API open OpenAI_API.Models open SerfBot.Log -let context = "Ты персональный помощник-бот в telegram. Чаще всего тебе нужно генерировать C#, F# или SQL код" +let context = "Ты персональный помощник-бот в telegram. Чаще всего тебе нужно генерировать C#, F# или SQL код, но иногда нужно и отвечать на бытовые вопросы." let conversationGPT userText = let openApiClient = OpenAIAPI(Configuration.config.OpenAiApiToken) @@ -13,7 +13,7 @@ let conversationGPT userText = conversation.AppendUserInput(userText); conversation.RequestParameters.Temperature <- 0.9; conversation.RequestParameters.MaxTokens <- 1024; - conversation.Model <- Model.ChatGPTTurbo; + conversation.Model <- Model.GPT4; conversation; let gptAnswer userQuestion = diff --git a/SerfBot/TelegramBot.fs b/SerfBot/TelegramBot.fs index 7f16340..d3e18bb 100644 --- a/SerfBot/TelegramBot.fs +++ b/SerfBot/TelegramBot.fs @@ -17,11 +17,6 @@ open SerfBot.TelegramApi type CommandHandler = string -> string -let commandHandlers : Dictionary = Dictionary() - -let addCommandHandler (command: string) (handler: CommandHandler) = - commandHandlers.Add(command.ToLower(), handler) - let handlePingCommand (command: string) = match command.ToLower() with | "ping" -> "pong" @@ -31,7 +26,8 @@ let handleWeatherCommand (command: string) = match command.Split(" ", 2) with | [| "погода"; location |] -> try - let weather = WeatherApi.getWeatherAsync location |> Async.RunSynchronously + let weather = WeatherApi.getWeatherAsync location + |> Async.RunSynchronously $"Погода в %s{location}: %s{weather}" with | ex -> sprintf "Ошибка при получении погоды: %s" ex.Message @@ -41,23 +37,19 @@ let handleWeatherCommand (command: string) = let handleGPTCommand (command: string) = match command.Split(" ", 2) with | [| "гпт"; inputText |] -> - let replayText = gptAnswer inputText |> Async.RunSynchronously; - $"%s{replayText}" - | _ -> "Неизвестная команда" + gptAnswer inputText + |> Async.RunSynchronously; + | _ -> "Неизвестная команда" + +let commandHandlers = + let handlers = Dictionary() + handlers.Add("ping", handlePingCommand) + handlers.Add("погода", handleWeatherCommand) + handlers.Add("гпт", handleGPTCommand) + handlers let extractCommand (str: string) = (str.Split(" ")[0]).Trim().ToLower(); -addCommandHandler "ping" handlePingCommand -addCommandHandler "погода" handleWeatherCommand -addCommandHandler "гпт" handleGPTCommand - -// obsolet -//let processCommand (ctx: UpdateContext, command: MessageReplayCommand) = -// Api.sendMessageReply command.Chat.Id command.ReplayText command.MessageId -// |> api ctx.Config -// |> Async.Ignore -// |> Async.Start - let processCommand (ctx: UpdateContext, command: MessageReplayCommand) = sendReplayMessageFormatted command.ReplayText ParseMode.Markdown ctx.Config api command.Chat.Id command.MessageId |> Async.RunSynchronously From 599711b17aca75180c0365ae1677d5c0eec19164 Mon Sep 17 00:00:00 2001 From: boogiedk Date: Sat, 14 Oct 2023 19:58:21 +0300 Subject: [PATCH 07/35] refactor --- SerfBot/TelegramBot.fs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/SerfBot/TelegramBot.fs b/SerfBot/TelegramBot.fs index d3e18bb..a9eac96 100644 --- a/SerfBot/TelegramBot.fs +++ b/SerfBot/TelegramBot.fs @@ -40,25 +40,26 @@ let handleGPTCommand (command: string) = gptAnswer inputText |> Async.RunSynchronously; | _ -> "Неизвестная команда" - + let commandHandlers = - let handlers = Dictionary() - handlers.Add("ping", handlePingCommand) - handlers.Add("погода", handleWeatherCommand) - handlers.Add("гпт", handleGPTCommand) - handlers + dict + [ + "ping", handlePingCommand + "погода", handleWeatherCommand + "гпт", handleGPTCommand + ] let extractCommand (str: string) = (str.Split(" ")[0]).Trim().ToLower(); +let isValidUser (userId: int64) = + if Array.contains userId Configuration.config.UserIds then Some () + else None + let processCommand (ctx: UpdateContext, command: MessageReplayCommand) = sendReplayMessageFormatted command.ReplayText ParseMode.Markdown ctx.Config api command.Chat.Id command.MessageId |> Async.RunSynchronously |> ignore -let isValidUser (userId: int64) = - if Array.contains userId Configuration.config.UserIds then Some () - else None - let updateArrived (ctx: UpdateContext) = match ctx.Update.Message with | Some { MessageId = messageId; Chat = chat; Text = text } -> From 0a341bc0c8ff49453ae528c35ec005dfa3445c8c Mon Sep 17 00:00:00 2001 From: boogiedk Date: Sun, 15 Oct 2023 11:43:05 +0300 Subject: [PATCH 08/35] add context command --- SerfBot/Commands.fs | 45 ++++++++++++++++++++++++++++++++++++++++++ SerfBot/OpenAiApi.fs | 14 +++++++++++-- SerfBot/Program.fs | 1 - SerfBot/SerfBot.fsproj | 1 + SerfBot/TelegramBot.fs | 42 +-------------------------------------- SerfBot/Types.fs | 8 +++++++- 6 files changed, 66 insertions(+), 45 deletions(-) create mode 100644 SerfBot/Commands.fs diff --git a/SerfBot/Commands.fs b/SerfBot/Commands.fs new file mode 100644 index 0000000..280f213 --- /dev/null +++ b/SerfBot/Commands.fs @@ -0,0 +1,45 @@ +module SerfBot.Commands + +open ExtCore.Control.Collections +open SerfBot.OpenAiApi; + +let handlePingCommand (command: string) = + match command.ToLower() with + | "ping" -> "pong" + | _ -> "Неизвестная команда" + +let handleWeatherCommand (command: string) = + match command.Split(" ", 2) with + | [| "погода"; location |] -> + try + let weather = WeatherApi.getWeatherAsync location + |> Async.RunSynchronously + $"Погода в %s{location}: %s{weather}" + with + | ex -> sprintf "Ошибка при получении погоды: %s" ex.Message + + | _ -> "Неизвестная команда" + +let handleGPTCommand (command: string) = + match command.Split(" ", 2) with + | [| "гпт"; inputText |] -> + gptAnswer inputText + |> Async.RunSynchronously; + | _ -> "Неизвестная команда" + +let handleContextCommand(command: string) = + match command.Split(" ", 2) with + | [| "!context"; inputText |] -> + setupContext inputText |> ignore + "Контекст сменен" + | _ -> "Неизвестная команда" + +let commandHandlers = + dict + [ + "ping", handlePingCommand + "погода", handleWeatherCommand + "гпт", handleGPTCommand + "!context", handleContextCommand + ] + diff --git a/SerfBot/OpenAiApi.fs b/SerfBot/OpenAiApi.fs index c1d7803..fe1460b 100644 --- a/SerfBot/OpenAiApi.fs +++ b/SerfBot/OpenAiApi.fs @@ -4,12 +4,22 @@ open OpenAI_API open OpenAI_API.Models open SerfBot.Log -let context = "Ты персональный помощник-бот в telegram. Чаще всего тебе нужно генерировать C#, F# или SQL код, но иногда нужно и отвечать на бытовые вопросы." +let mutable currentContext = None + +let setupContext (newContext: string) = + match newContext with + | null -> + match currentContext with + | None -> "Ты персональный помощник-бот в telegram. Чаще всего тебе нужно генерировать C#, F# или SQL код, но иногда нужно и отвечать на бытовые вопросы." + | Some x -> x + | _ -> + currentContext <- Some newContext + newContext let conversationGPT userText = let openApiClient = OpenAIAPI(Configuration.config.OpenAiApiToken) let conversation = openApiClient.Chat.CreateConversation() - conversation.AppendSystemMessage(context) + conversation.AppendSystemMessage(Option.get currentContext) conversation.AppendUserInput(userText); conversation.RequestParameters.Temperature <- 0.9; conversation.RequestParameters.MaxTokens <- 1024; diff --git a/SerfBot/Program.fs b/SerfBot/Program.fs index 0472d3a..61db2cc 100644 --- a/SerfBot/Program.fs +++ b/SerfBot/Program.fs @@ -8,7 +8,6 @@ open SerfBot.Log open SerfBot.TelegramBot open Types open Serilog -open Log; module Program = diff --git a/SerfBot/SerfBot.fsproj b/SerfBot/SerfBot.fsproj index b8bbcad..83c4530 100644 --- a/SerfBot/SerfBot.fsproj +++ b/SerfBot/SerfBot.fsproj @@ -15,6 +15,7 @@ + diff --git a/SerfBot/TelegramBot.fs b/SerfBot/TelegramBot.fs index a9eac96..9b6ee39 100644 --- a/SerfBot/TelegramBot.fs +++ b/SerfBot/TelegramBot.fs @@ -1,54 +1,14 @@ module SerfBot.TelegramBot open System -open System.Runtime.CompilerServices open ExtCore.Control.Collections open Funogram.Api -open Funogram.Telegram open Funogram.Telegram.Bot -open System.Collections.Generic open Funogram.Telegram.Types open SerfBot.Log -open SerfBot.OpenAiApi; open SerfBot.Types -open Telegram.Bot.Types; -open System.Text.RegularExpressions open SerfBot.TelegramApi -type CommandHandler = string -> string - -let handlePingCommand (command: string) = - match command.ToLower() with - | "ping" -> "pong" - | _ -> "Неизвестная команда" - -let handleWeatherCommand (command: string) = - match command.Split(" ", 2) with - | [| "погода"; location |] -> - try - let weather = WeatherApi.getWeatherAsync location - |> Async.RunSynchronously - $"Погода в %s{location}: %s{weather}" - with - | ex -> sprintf "Ошибка при получении погоды: %s" ex.Message - - | _ -> "Неизвестная команда" - -let handleGPTCommand (command: string) = - match command.Split(" ", 2) with - | [| "гпт"; inputText |] -> - gptAnswer inputText - |> Async.RunSynchronously; - | _ -> "Неизвестная команда" - -let commandHandlers = - dict - [ - "ping", handlePingCommand - "погода", handleWeatherCommand - "гпт", handleGPTCommand - ] - let extractCommand (str: string) = (str.Split(" ")[0]).Trim().ToLower(); let isValidUser (userId: int64) = @@ -69,7 +29,7 @@ let updateArrived (ctx: UpdateContext) = logInfo $"Message from user {Option.get user.Username} received: {Option.get text}" let userMessage = text.Value; let command = extractCommand userMessage - match commandHandlers.TryGetValue command with + match Commands.commandHandlers.TryGetValue command with | true, handler -> let replyText = handler userMessage processCommand(ctx, { Chat = chat; MessageId = messageId; Text = text; ReplayText = replyText; }) diff --git a/SerfBot/Types.fs b/SerfBot/Types.fs index 891433f..b622cd6 100644 --- a/SerfBot/Types.fs +++ b/SerfBot/Types.fs @@ -1,7 +1,6 @@ module SerfBot.Types open Funogram.Telegram -open Funogram.Telegram.Types type CityCoordinates = { CityName: string @@ -32,3 +31,10 @@ type ApplicationConfiguration = { UserIds: int64[] } +type CommandHandler = string -> string + +type Commands = { + Name: string + Handler: CommandHandler +} + From 6802f0f1f2ee1ed6e5786067710e2629de7c0180 Mon Sep 17 00:00:00 2001 From: boogiedk Date: Sun, 15 Oct 2023 16:37:38 +0300 Subject: [PATCH 09/35] refactoring commands --- SerfBot/Commands.fs | 46 ++++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/SerfBot/Commands.fs b/SerfBot/Commands.fs index 280f213..adca9b1 100644 --- a/SerfBot/Commands.fs +++ b/SerfBot/Commands.fs @@ -3,36 +3,34 @@ open ExtCore.Control.Collections open SerfBot.OpenAiApi; -let handlePingCommand (command: string) = - match command.ToLower() with - | "ping" -> "pong" - | _ -> "Неизвестная команда" -let handleWeatherCommand (command: string) = - match command.Split(" ", 2) with - | [| "погода"; location |] -> +let pingCommand() = + "pong" + +let handlePingCommand (command: string) = "pong" + +let handleWeatherCommand (userText: string) = try - let weather = WeatherApi.getWeatherAsync location + let weather = WeatherApi.getWeatherAsync userText |> Async.RunSynchronously - $"Погода в %s{location}: %s{weather}" + $"Погода в %s{userText}: %s{weather}" with - | ex -> sprintf "Ошибка при получении погоды: %s" ex.Message - - | _ -> "Неизвестная команда" + | ex -> sprintf "Ошибка: %s" ex.Message -let handleGPTCommand (command: string) = - match command.Split(" ", 2) with - | [| "гпт"; inputText |] -> - gptAnswer inputText - |> Async.RunSynchronously; - | _ -> "Неизвестная команда" +let handleGPTCommand (userText: string) = + try + gptAnswer userText + |> Async.RunSynchronously + with + | ex -> sprintf "Ошибка: %s" ex.Message -let handleContextCommand(command: string) = - match command.Split(" ", 2) with - | [| "!context"; inputText |] -> - setupContext inputText |> ignore - "Контекст сменен" - | _ -> "Неизвестная команда" +let handleContextCommand(userText: string) = + try + setupContext userText + |> ignore + "Контекст сменен" + with + | ex -> sprintf "Ошибка: %s" ex.Message let commandHandlers = dict From bdfd2af828eebde50cfc122b77b3f5d303ea5577 Mon Sep 17 00:00:00 2001 From: boogiedk Date: Sun, 15 Oct 2023 16:43:56 +0300 Subject: [PATCH 10/35] fix --- SerfBot/TelegramBot.fs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/SerfBot/TelegramBot.fs b/SerfBot/TelegramBot.fs index 9b6ee39..843dddd 100644 --- a/SerfBot/TelegramBot.fs +++ b/SerfBot/TelegramBot.fs @@ -9,7 +9,10 @@ open SerfBot.Log open SerfBot.Types open SerfBot.TelegramApi -let extractCommand (str: string) = (str.Split(" ")[0]).Trim().ToLower(); +let extractCommand (str: string) = + let command = (str.Split(" ")[0]).Trim().ToLower() + let userMessage = (str.Split(" ")[1]).Trim().ToLower() + (command, userMessage) let isValidUser (userId: int64) = if Array.contains userId Configuration.config.UserIds then Some () @@ -27,8 +30,7 @@ let updateArrived (ctx: UpdateContext) = match isValidUser user.Id with | Some () -> logInfo $"Message from user {Option.get user.Username} received: {Option.get text}" - let userMessage = text.Value; - let command = extractCommand userMessage + let command, userMessage = extractCommand text.Value match Commands.commandHandlers.TryGetValue command with | true, handler -> let replyText = handler userMessage From f90be41094f358a220425ff8a1bd4ba4bfb725e7 Mon Sep 17 00:00:00 2001 From: boogiedk Date: Sun, 15 Oct 2023 16:47:58 +0300 Subject: [PATCH 11/35] fix --- SerfBot/TelegramBot.fs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/SerfBot/TelegramBot.fs b/SerfBot/TelegramBot.fs index 843dddd..1ac137b 100644 --- a/SerfBot/TelegramBot.fs +++ b/SerfBot/TelegramBot.fs @@ -10,8 +10,9 @@ open SerfBot.Types open SerfBot.TelegramApi let extractCommand (str: string) = - let command = (str.Split(" ")[0]).Trim().ToLower() - let userMessage = (str.Split(" ")[1]).Trim().ToLower() + let message = str.Split(" "); + let command = message[0].Trim().ToLower() + let userMessage = str.Split(" ")[1] (command, userMessage) let isValidUser (userId: int64) = From c6ebb0eda56e55dbfbde284d4c0f34978312b42f Mon Sep 17 00:00:00 2001 From: boogiedk Date: Sun, 15 Oct 2023 17:08:42 +0300 Subject: [PATCH 12/35] fix --- SerfBot/OpenAiApi.fs | 5 +++-- SerfBot/TelegramBot.fs | 9 +++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/SerfBot/OpenAiApi.fs b/SerfBot/OpenAiApi.fs index fe1460b..ca530fe 100644 --- a/SerfBot/OpenAiApi.fs +++ b/SerfBot/OpenAiApi.fs @@ -4,13 +4,14 @@ open OpenAI_API open OpenAI_API.Models open SerfBot.Log -let mutable currentContext = None +let defaultContext = "Ты персональный помощник-бот в telegram. Чаще всего тебе нужно генерировать C#, F# или SQL код, но иногда нужно и отвечать на бытовые вопросы." +let mutable currentContext = Some(defaultContext) let setupContext (newContext: string) = match newContext with | null -> match currentContext with - | None -> "Ты персональный помощник-бот в telegram. Чаще всего тебе нужно генерировать C#, F# или SQL код, но иногда нужно и отвечать на бытовые вопросы." + | None -> defaultContext | Some x -> x | _ -> currentContext <- Some newContext diff --git a/SerfBot/TelegramBot.fs b/SerfBot/TelegramBot.fs index 1ac137b..22d5cae 100644 --- a/SerfBot/TelegramBot.fs +++ b/SerfBot/TelegramBot.fs @@ -10,10 +10,11 @@ open SerfBot.Types open SerfBot.TelegramApi let extractCommand (str: string) = - let message = str.Split(" "); - let command = message[0].Trim().ToLower() - let userMessage = str.Split(" ")[1] - (command, userMessage) + match str.Split(" ", 2) with + | [| command; inputText |] -> + (command, inputText) + | [| command; |] -> + (command, null) let isValidUser (userId: int64) = if Array.contains userId Configuration.config.UserIds then Some () From 2ce89241db47cb5571559ae6a53124df452a6f14 Mon Sep 17 00:00:00 2001 From: boogiedk Date: Thu, 16 Nov 2023 10:40:01 +0300 Subject: [PATCH 13/35] redeploy --- SerfBot/SerfBot.fsproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SerfBot/SerfBot.fsproj b/SerfBot/SerfBot.fsproj index 83c4530..1655792 100644 --- a/SerfBot/SerfBot.fsproj +++ b/SerfBot/SerfBot.fsproj @@ -32,7 +32,7 @@ - + From 4a8fcabb85edadafdb8a15cfa7cfcc47606e1ae7 Mon Sep 17 00:00:00 2001 From: boogiedk Date: Thu, 16 Nov 2023 10:47:55 +0300 Subject: [PATCH 14/35] fix token --- SerfBot/appsettings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SerfBot/appsettings.json b/SerfBot/appsettings.json index dc3b570..3033aa3 100644 --- a/SerfBot/appsettings.json +++ b/SerfBot/appsettings.json @@ -3,5 +3,5 @@ 202224486 ], "TelegramBotToken": "270652017:AAG5O6FvMgMhB9MlOBsIF_yoKXaoiHD1GGs", - "OpenAiApiToken": "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" + "OpenAiApiToken": "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" } \ No newline at end of file From 7df40c8ae7baf8b4d2a65a872e4f685f3f3b56a6 Mon Sep 17 00:00:00 2001 From: boogiedk Date: Thu, 16 Nov 2023 16:08:47 +0300 Subject: [PATCH 15/35] add vision --- SerfBot/Commands.fs | 10 ++++- SerfBot/OpenAiApi.fs | 89 +++++++++++++++++++++++++++++++++--------- SerfBot/SerfBot.fsproj | 5 ++- 3 files changed, 83 insertions(+), 21 deletions(-) diff --git a/SerfBot/Commands.fs b/SerfBot/Commands.fs index adca9b1..62bc91e 100644 --- a/SerfBot/Commands.fs +++ b/SerfBot/Commands.fs @@ -31,13 +31,21 @@ let handleContextCommand(userText: string) = "Контекст сменен" with | ex -> sprintf "Ошибка: %s" ex.Message + +let handleVisionCommand (imageLink: string) = + try + descriptionAnalyzedImage imageLink + |> Async.RunSynchronously + with + | ex -> sprintf "Ошибка: %s" ex.Message let commandHandlers = dict [ - "ping", handlePingCommand + "!ping", handlePingCommand "погода", handleWeatherCommand "гпт", handleGPTCommand "!context", handleContextCommand + "!vision", handleVisionCommand ] diff --git a/SerfBot/OpenAiApi.fs b/SerfBot/OpenAiApi.fs index ca530fe..2c24613 100644 --- a/SerfBot/OpenAiApi.fs +++ b/SerfBot/OpenAiApi.fs @@ -1,8 +1,13 @@ module SerfBot.OpenAiApi -open OpenAI_API -open OpenAI_API.Models -open SerfBot.Log +open OpenAI +open OpenAI.Managers +open OpenAI.ObjectModels +open OpenAI.ObjectModels.RequestModels +open SerfBot.Types +open OpenAI.Chat +open System + let defaultContext = "Ты персональный помощник-бот в telegram. Чаще всего тебе нужно генерировать C#, F# или SQL код, но иногда нужно и отвечать на бытовые вопросы." let mutable currentContext = Some(defaultContext) @@ -17,25 +22,71 @@ let setupContext (newContext: string) = currentContext <- Some newContext newContext -let conversationGPT userText = - let openApiClient = OpenAIAPI(Configuration.config.OpenAiApiToken) - let conversation = openApiClient.Chat.CreateConversation() - conversation.AppendSystemMessage(Option.get currentContext) - conversation.AppendUserInput(userText); - conversation.RequestParameters.Temperature <- 0.9; - conversation.RequestParameters.MaxTokens <- 1024; - conversation.Model <- Model.GPT4; - conversation; +let conversationGPT = + let token = Configuration.config.OpenAiApiToken + let options = OpenAiOptions() + options.ApiKey <- token + + let openApiClient = new OpenAIService(options) + + openApiClient + +let gptQuestionRequest userText = + let messages = + [ + ChatMessage.FromSystem(Option.get currentContext) + ChatMessage.FromUser(userText) + ] |> List.toArray + + ChatCompletionCreateRequest(Messages = messages, Model = Models.Gpt_4) let gptAnswer userQuestion = async { try - let conv = conversationGPT userQuestion - let! result = conv.GetResponseFromChatbotAsync() |> Async.AwaitTask + let conv = conversationGPT + let request = gptQuestionRequest userQuestion + + let! completionResult = conv.CreateCompletion(request) |> Async.AwaitTask + + let result = + if completionResult.Successful then + completionResult.Choices |> Seq.head |> fun c -> c.Message.Content + else + match completionResult.Error with + | null -> raise (Exception("Unknown Error")) + | error -> sprintf "%s: %s" error.Code error.Message + return result + + with + | ex -> return ex.Message + } + +let descriptionAnalyzedImage imageLink = + async { + try + let api = OpenAIClient(Configuration.config.OpenAiApiToken) + + let messages = + [ + Message(Role.System, Option.get currentContext) + Message(Role.User, + [ + Content(ContentType.Text, "Что на этой картинке?") + Content(ContentType.ImageUrl, imageLink) + ]) + ] + + let result = + async { + let! completionResult = api.ChatEndpoint.GetCompletionAsync(ChatRequest(messages, model = "gpt-4-vision-preview", maxTokens = 500)) |> Async.AwaitTask + return completionResult + } + |> Async.RunSynchronously + + let answer = result.FirstChoice.Message.Content.ToString() + return answer + with - | ex -> - let errorText = sprintf "Exception text: %s" (ex.Message) - logErr errorText - return errorText - } \ No newline at end of file + | ex -> return ex.Message.ToString() + } \ No newline at end of file diff --git a/SerfBot/SerfBot.fsproj b/SerfBot/SerfBot.fsproj index 1655792..1b7cd93 100644 --- a/SerfBot/SerfBot.fsproj +++ b/SerfBot/SerfBot.fsproj @@ -21,13 +21,16 @@ + + + - + From a3e4878ba63a7f61c468a074c69461c7f58b529c Mon Sep 17 00:00:00 2001 From: boogiedk Date: Thu, 16 Nov 2023 16:42:46 +0300 Subject: [PATCH 16/35] logs error --- SerfBot/OpenAiApi.fs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/SerfBot/OpenAiApi.fs b/SerfBot/OpenAiApi.fs index 2c24613..c92385b 100644 --- a/SerfBot/OpenAiApi.fs +++ b/SerfBot/OpenAiApi.fs @@ -7,6 +7,7 @@ open OpenAI.ObjectModels.RequestModels open SerfBot.Types open OpenAI.Chat open System +open Log let defaultContext = "Ты персональный помощник-бот в telegram. Чаще всего тебе нужно генерировать C#, F# или SQL код, но иногда нужно и отвечать на бытовые вопросы." @@ -53,13 +54,19 @@ let gptAnswer userQuestion = completionResult.Choices |> Seq.head |> fun c -> c.Message.Content else match completionResult.Error with - | null -> raise (Exception("Unknown Error")) - | error -> sprintf "%s: %s" error.Code error.Message + | null -> + "Unknown Error" |> logInfo + raise (Exception("Unknown Error")) + | error -> + $"{error.Code} {error.Message}" |> logInfo + sprintf "%s: %s" error.Code error.Message return result with - | ex -> return ex.Message + | ex -> + ex.Message |> logInfo + return ex.Message } let descriptionAnalyzedImage imageLink = @@ -88,5 +95,7 @@ let descriptionAnalyzedImage imageLink = return answer with - | ex -> return ex.Message.ToString() + | ex -> + ex.Message.ToString() |> logInfo + return ex.Message.ToString() } \ No newline at end of file From 7035298ad9790cf8ef9af2882e859c97e48e782d Mon Sep 17 00:00:00 2001 From: boogiedk Date: Wed, 6 Dec 2023 23:21:33 +0300 Subject: [PATCH 17/35] refactor --- SerfBot/Commands.fs | 69 ++++++++++++++---------------------------- SerfBot/Program.fs | 2 +- SerfBot/TelegramBot.fs | 56 ++++++++++++++++++++++++---------- SerfBot/Types.fs | 8 +++++ 4 files changed, 71 insertions(+), 64 deletions(-) diff --git a/SerfBot/Commands.fs b/SerfBot/Commands.fs index 62bc91e..4386cea 100644 --- a/SerfBot/Commands.fs +++ b/SerfBot/Commands.fs @@ -2,50 +2,25 @@ open ExtCore.Control.Collections open SerfBot.OpenAiApi; - - -let pingCommand() = - "pong" - -let handlePingCommand (command: string) = "pong" - -let handleWeatherCommand (userText: string) = - try - let weather = WeatherApi.getWeatherAsync userText - |> Async.RunSynchronously - $"Погода в %s{userText}: %s{weather}" - with - | ex -> sprintf "Ошибка: %s" ex.Message - -let handleGPTCommand (userText: string) = - try - gptAnswer userText - |> Async.RunSynchronously - with - | ex -> sprintf "Ошибка: %s" ex.Message - -let handleContextCommand(userText: string) = - try - setupContext userText - |> ignore - "Контекст сменен" - with - | ex -> sprintf "Ошибка: %s" ex.Message - -let handleVisionCommand (imageLink: string) = - try - descriptionAnalyzedImage imageLink - |> Async.RunSynchronously - with - | ex -> sprintf "Ошибка: %s" ex.Message - -let commandHandlers = - dict - [ - "!ping", handlePingCommand - "погода", handleWeatherCommand - "гпт", handleGPTCommand - "!context", handleContextCommand - "!vision", handleVisionCommand - ] - +open SerfBot.Types + +let commandHandler command = + try + match command with + | Ping -> "pong" + | Vision (imageLink, userText) -> + descriptionAnalyzedImage imageLink + |> Async.RunSynchronously + | Context userText -> + setupContext userText + |> ignore + "Контекст сменен" + | Question userText -> + gptAnswer userText + |> Async.RunSynchronously + | Weather city -> + let weather = WeatherApi.getWeatherAsync city |> Async.RunSynchronously + $"Погода в %s{city}: %s{weather}" + | _ -> "Некорректная команда для GPT" + with + | ex -> sprintf "Ошибка: %s" ex.Message \ No newline at end of file diff --git a/SerfBot/Program.fs b/SerfBot/Program.fs index 61db2cc..a4c0f36 100644 --- a/SerfBot/Program.fs +++ b/SerfBot/Program.fs @@ -23,7 +23,7 @@ module Program = let! _ = Api.deleteWebhookBase () |> api telegramBotConfig logInfo "SerfBot start" - return! startBot telegramBotConfig updateArrived None + return! startBot telegramBotConfig updateArrivedMessage None } |> Async.RunSynchronously logInfo "SefBot stopped" diff --git a/SerfBot/TelegramBot.fs b/SerfBot/TelegramBot.fs index 22d5cae..283570d 100644 --- a/SerfBot/TelegramBot.fs +++ b/SerfBot/TelegramBot.fs @@ -25,20 +25,44 @@ let processCommand (ctx: UpdateContext, command: MessageReplayCommand) = |> Async.RunSynchronously |> ignore -let updateArrived (ctx: UpdateContext) = - match ctx.Update.Message with - | Some { MessageId = messageId; Chat = chat; Text = text } -> - let user = ctx.Update.Message.Value.From.Value - match isValidUser user.Id with - | Some () -> - logInfo $"Message from user {Option.get user.Username} received: {Option.get text}" - let command, userMessage = extractCommand text.Value - match Commands.commandHandlers.TryGetValue command with - | true, handler -> - let replyText = handler userMessage - processCommand(ctx, { Chat = chat; MessageId = messageId; Text = text; ReplayText = replyText; }) +// obsolet +//let updateArrived (ctx: UpdateContext) = +// match ctx.Update.Message with +// | Some { MessageId = messageId; Chat = chat; Text = text; Photo = photo; Voice = voice } -> +// let user = ctx.Update.Message.Value.From.Value +// match isValidUser user.Id with +// | Some () -> +// logInfo $"Message from user {Option.get user.Username} received: {Option.get text}" +// let command, userMessage = extractCommand text.Value +// match Commands.commandHandlers.TryGetValue command with +// | true, handler -> +// let replyText = handler userMessage +// processCommand(ctx, { Chat = chat; MessageId = messageId; Text = text; ReplayText = replyText; }) +// | _ -> () +// | None -> +// sprintf "Authorize error." |> logInfo +// | _ -> () + + +let updateArrivedMessage (ctx: UpdateContext) = + match ctx.Update.Message with + | Some { MessageId = messageId; Chat = chat; Text = text; Photo = photo } -> + let user = ctx.Update.Message.Value.From.Value + match isValidUser user.Id with + | Some () -> + logInfo $"Message from user {Option.get user.Username} received: {Option.get text}" + let command, userMessage = extractCommand text.Value + let commandType = + match command with + | "!ping" -> Ping + | "погода" -> Weather userMessage + | "!context" -> Context userMessage + | "!vision" -> Vision (userMessage, "test") + | "гпт" -> Question userMessage + | _ -> Other userMessage + + let replyText = Commands.commandHandler commandType + processCommand(ctx, { Chat = chat; MessageId = messageId; Text = text; ReplayText = replyText }) | _ -> () - | None -> - sprintf "Authorize error." |> logInfo - | _ -> () - \ No newline at end of file + | None -> sprintf "Authorize error." |> logInfo + | _ -> () \ No newline at end of file diff --git a/SerfBot/Types.fs b/SerfBot/Types.fs index b622cd6..3a5b704 100644 --- a/SerfBot/Types.fs +++ b/SerfBot/Types.fs @@ -38,3 +38,11 @@ type Commands = { Handler: CommandHandler } +type Command = + | Ping + | Question of string + | Context of string + | Vision of string * string + | Weather of string + | Other of string + From 34c9e27219e5f543f9585d07d3b2857a858256b7 Mon Sep 17 00:00:00 2001 From: boogiedk Date: Thu, 7 Dec 2023 00:37:51 +0300 Subject: [PATCH 18/35] fix vision --- SerfBot/Commands.fs | 9 ++++--- SerfBot/OpenAiApi.fs | 8 +++--- SerfBot/TelegramBot.fs | 55 ++++++++++++++++++++++++++---------------- SerfBot/Types.fs | 7 ------ 4 files changed, 43 insertions(+), 36 deletions(-) diff --git a/SerfBot/Commands.fs b/SerfBot/Commands.fs index 4386cea..938ad62 100644 --- a/SerfBot/Commands.fs +++ b/SerfBot/Commands.fs @@ -8,8 +8,8 @@ let commandHandler command = try match command with | Ping -> "pong" - | Vision (imageLink, userText) -> - descriptionAnalyzedImage imageLink + | Vision (userText, base64Img) -> + descriptionAnalyzedImage userText base64Img |> Async.RunSynchronously | Context userText -> setupContext userText @@ -19,8 +19,9 @@ let commandHandler command = gptAnswer userText |> Async.RunSynchronously | Weather city -> - let weather = WeatherApi.getWeatherAsync city |> Async.RunSynchronously + let weather = WeatherApi.getWeatherAsync city + |> Async.RunSynchronously $"Погода в %s{city}: %s{weather}" - | _ -> "Некорректная команда для GPT" + | _ -> "Некорректная команда" with | ex -> sprintf "Ошибка: %s" ex.Message \ No newline at end of file diff --git a/SerfBot/OpenAiApi.fs b/SerfBot/OpenAiApi.fs index c92385b..72792ed 100644 --- a/SerfBot/OpenAiApi.fs +++ b/SerfBot/OpenAiApi.fs @@ -69,18 +69,18 @@ let gptAnswer userQuestion = return ex.Message } -let descriptionAnalyzedImage imageLink = +let descriptionAnalyzedImage userText base64Img = async { try let api = OpenAIClient(Configuration.config.OpenAiApiToken) - + let userText2 = if userText == null then "Что на фото?" else userText let messages = [ Message(Role.System, Option.get currentContext) Message(Role.User, [ - Content(ContentType.Text, "Что на этой картинке?") - Content(ContentType.ImageUrl, imageLink) + Content(ContentType.Text, userText2) + Content(ContentType.ImageUrl, $"data:image/jpeg;base64,{base64Img}") ]) ] diff --git a/SerfBot/TelegramBot.fs b/SerfBot/TelegramBot.fs index 283570d..b5b0a4e 100644 --- a/SerfBot/TelegramBot.fs +++ b/SerfBot/TelegramBot.fs @@ -1,8 +1,11 @@ module SerfBot.TelegramBot open System +open System.IO +open System.Net.Http open ExtCore.Control.Collections open Funogram.Api +open Funogram.Telegram open Funogram.Telegram.Bot open Funogram.Telegram.Types open SerfBot.Log @@ -25,39 +28,49 @@ let processCommand (ctx: UpdateContext, command: MessageReplayCommand) = |> Async.RunSynchronously |> ignore -// obsolet -//let updateArrived (ctx: UpdateContext) = -// match ctx.Update.Message with -// | Some { MessageId = messageId; Chat = chat; Text = text; Photo = photo; Voice = voice } -> -// let user = ctx.Update.Message.Value.From.Value -// match isValidUser user.Id with -// | Some () -> -// logInfo $"Message from user {Option.get user.Username} received: {Option.get text}" -// let command, userMessage = extractCommand text.Value -// match Commands.commandHandlers.TryGetValue command with -// | true, handler -> -// let replyText = handler userMessage -// processCommand(ctx, { Chat = chat; MessageId = messageId; Text = text; ReplayText = replyText; }) -// | _ -> () -// | None -> -// sprintf "Authorize error." |> logInfo -// | _ -> () +let streamToBase64 (stream: Stream) = + use ms = new MemoryStream() + stream.CopyTo(ms) + let buffer = ms.ToArray() + Convert.ToBase64String(buffer) +let extractFileDataAsBase64 (fileResult: Result) = + match fileResult with + | Ok(file) -> + let filePath = Option.get file.FilePath // предполагается, что у вас есть свойство "Path" в типе "File" + let apiUrl = $"https://api.telegram.org/file/bot{Configuration.config.TelegramBotToken}/{filePath}" + use httpStream = new HttpClient() + let f = httpStream.GetStreamAsync(apiUrl) |> Async.AwaitTask |> Async.RunSynchronously + let base64 = streamToBase64 f + base64 + + +let handleFiles fileId ctx = + let file = Req.GetFile.Make fileId + |> api ctx.Config + |> Async.RunSynchronously + let base64Img = extractFileDataAsBase64 file + base64Img + + let updateArrivedMessage (ctx: UpdateContext) = match ctx.Update.Message with - | Some { MessageId = messageId; Chat = chat; Text = text; Photo = photo } -> + | Some { MessageId = messageId; Chat = chat; Text = text; Photo = photo; Caption = caption } -> let user = ctx.Update.Message.Value.From.Value + + let base64Img = if photo.IsSome then handleFiles photo.Value[0].FileId ctx else "" + let message = if text.IsSome then text.Value elif caption.IsSome then caption.Value else "" match isValidUser user.Id with | Some () -> - logInfo $"Message from user {Option.get user.Username} received: {Option.get text}" - let command, userMessage = extractCommand text.Value + logInfo $"Message from user {Option.get user.Username} received: " //{Option.get text} + let command, userMessage = extractCommand message let commandType = match command with | "!ping" -> Ping | "погода" -> Weather userMessage | "!context" -> Context userMessage - | "!vision" -> Vision (userMessage, "test") + | "!vision" -> Vision (userMessage, base64Img) | "гпт" -> Question userMessage | _ -> Other userMessage diff --git a/SerfBot/Types.fs b/SerfBot/Types.fs index 3a5b704..9d56f26 100644 --- a/SerfBot/Types.fs +++ b/SerfBot/Types.fs @@ -31,13 +31,6 @@ type ApplicationConfiguration = { UserIds: int64[] } -type CommandHandler = string -> string - -type Commands = { - Name: string - Handler: CommandHandler -} - type Command = | Ping | Question of string From b1324d9b8896c9379555845873294f197f610aad Mon Sep 17 00:00:00 2001 From: boogiedk Date: Thu, 7 Dec 2023 00:53:41 +0300 Subject: [PATCH 19/35] fix fileId get --- SerfBot/TelegramBot.fs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SerfBot/TelegramBot.fs b/SerfBot/TelegramBot.fs index b5b0a4e..152fae7 100644 --- a/SerfBot/TelegramBot.fs +++ b/SerfBot/TelegramBot.fs @@ -59,11 +59,11 @@ let updateArrivedMessage (ctx: UpdateContext) = | Some { MessageId = messageId; Chat = chat; Text = text; Photo = photo; Caption = caption } -> let user = ctx.Update.Message.Value.From.Value - let base64Img = if photo.IsSome then handleFiles photo.Value[0].FileId ctx else "" + let base64Img = if photo.IsSome then handleFiles (Array.last photo.Value).FileId ctx else "" let message = if text.IsSome then text.Value elif caption.IsSome then caption.Value else "" match isValidUser user.Id with | Some () -> - logInfo $"Message from user {Option.get user.Username} received: " //{Option.get text} + logInfo $"Message from user {Option.get user.Username} received: {message}" let command, userMessage = extractCommand message let commandType = match command with From 3052525ca9df6a7cd5fc6833f72a1b8b9ad79780 Mon Sep 17 00:00:00 2001 From: boogiedk Date: Mon, 11 Dec 2023 14:00:03 +0300 Subject: [PATCH 20/35] external appsettings.json --- Dockerfile | 1 + SerfBot/TelegramBot.fs | 1 - SerfBot/appsettings.json | 8 +++----- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index ca29eac..6d0cdf9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,4 +16,5 @@ RUN dotnet publish "SerfBot.fsproj" -c Release -o /app/publish FROM base AS final WORKDIR /app COPY --from=publish /app/publish . +COPY /home/SerfBot/appsettings.json ./appsettings.json ENTRYPOINT ["dotnet", "SerfBot.dll"] \ No newline at end of file diff --git a/SerfBot/TelegramBot.fs b/SerfBot/TelegramBot.fs index 152fae7..3ab5542 100644 --- a/SerfBot/TelegramBot.fs +++ b/SerfBot/TelegramBot.fs @@ -58,7 +58,6 @@ let updateArrivedMessage (ctx: UpdateContext) = match ctx.Update.Message with | Some { MessageId = messageId; Chat = chat; Text = text; Photo = photo; Caption = caption } -> let user = ctx.Update.Message.Value.From.Value - let base64Img = if photo.IsSome then handleFiles (Array.last photo.Value).FileId ctx else "" let message = if text.IsSome then text.Value elif caption.IsSome then caption.Value else "" match isValidUser user.Id with diff --git a/SerfBot/appsettings.json b/SerfBot/appsettings.json index 3033aa3..5b32104 100644 --- a/SerfBot/appsettings.json +++ b/SerfBot/appsettings.json @@ -1,7 +1,5 @@ { - "UserIds": [ - 202224486 - ], - "TelegramBotToken": "270652017:AAG5O6FvMgMhB9MlOBsIF_yoKXaoiHD1GGs", - "OpenAiApiToken": "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" + "UserIds": [], + "TelegramBotToken": "bot-token", + "OpenAiApiToken": "openai-token" } \ No newline at end of file From 306eb40eff738ea2efeae2c913e96b4c2719f3b6 Mon Sep 17 00:00:00 2001 From: boogiedk Date: Mon, 11 Dec 2023 14:03:39 +0300 Subject: [PATCH 21/35] fix docker --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 6d0cdf9..62b335a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,5 +16,5 @@ RUN dotnet publish "SerfBot.fsproj" -c Release -o /app/publish FROM base AS final WORKDIR /app COPY --from=publish /app/publish . -COPY /home/SerfBot/appsettings.json ./appsettings.json +COPY /home/root/appsettings.json ./appsettings.json ENTRYPOINT ["dotnet", "SerfBot.dll"] \ No newline at end of file From 4665a7658242a912f937aa325824efeddef4e230 Mon Sep 17 00:00:00 2001 From: boogiedk Date: Mon, 11 Dec 2023 14:10:41 +0300 Subject: [PATCH 22/35] fix --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 62b335a..fc95b21 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,5 +16,5 @@ RUN dotnet publish "SerfBot.fsproj" -c Release -o /app/publish FROM base AS final WORKDIR /app COPY --from=publish /app/publish . -COPY /home/root/appsettings.json ./appsettings.json +COPY /home/root/appsettings.json /app/appsettings.json ENTRYPOINT ["dotnet", "SerfBot.dll"] \ No newline at end of file From c48bb78e0f9353ede0ed41e998b2cfb1b257a652 Mon Sep 17 00:00:00 2001 From: boogiedk Date: Mon, 11 Dec 2023 14:14:57 +0300 Subject: [PATCH 23/35] fix --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index fc95b21..8e6b390 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,5 +16,5 @@ RUN dotnet publish "SerfBot.fsproj" -c Release -o /app/publish FROM base AS final WORKDIR /app COPY --from=publish /app/publish . -COPY /home/root/appsettings.json /app/appsettings.json +COPY ./home/root/appsettings.json /app/appsettings.json ENTRYPOINT ["dotnet", "SerfBot.dll"] \ No newline at end of file From 546a24658916904c9379fe97f719f088eacf4028 Mon Sep 17 00:00:00 2001 From: boogiedk Date: Mon, 11 Dec 2023 14:17:14 +0300 Subject: [PATCH 24/35] fix --- Dockerfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 8e6b390..da09dd3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,5 +16,7 @@ RUN dotnet publish "SerfBot.fsproj" -c Release -o /app/publish FROM base AS final WORKDIR /app COPY --from=publish /app/publish . -COPY ./home/root/appsettings.json /app/appsettings.json +RUN ls -l /home/ +RUN ls -l /home/root/appsettings.json +COPY /home/root/appsettings.json /app/appsettings.json ENTRYPOINT ["dotnet", "SerfBot.dll"] \ No newline at end of file From 80ef8a57734381cb3709361e845b00f4b0e8c4a3 Mon Sep 17 00:00:00 2001 From: boogiedk Date: Mon, 11 Dec 2023 14:19:59 +0300 Subject: [PATCH 25/35] fix --- Dockerfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index da09dd3..81fbf99 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,6 +7,9 @@ WORKDIR /src COPY ["SerfBot/SerfBot.fsproj", "SerfBot/"] RUN dotnet restore "SerfBot/SerfBot.fsproj" COPY . . + +COPY /home/root/appsettings.json /app/appsettings.json + WORKDIR "/src/SerfBot" RUN dotnet build "SerfBot.fsproj" -c Release -o /app/build @@ -16,7 +19,4 @@ RUN dotnet publish "SerfBot.fsproj" -c Release -o /app/publish FROM base AS final WORKDIR /app COPY --from=publish /app/publish . -RUN ls -l /home/ -RUN ls -l /home/root/appsettings.json -COPY /home/root/appsettings.json /app/appsettings.json ENTRYPOINT ["dotnet", "SerfBot.dll"] \ No newline at end of file From 2761fcbe6744895a9289ce5cf57fd50c3a0841b1 Mon Sep 17 00:00:00 2001 From: boogiedk Date: Mon, 11 Dec 2023 14:25:01 +0300 Subject: [PATCH 26/35] fix --- Dockerfile | 3 +-- SerfBot/appsettings.json | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 81fbf99..3c0ad51 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,8 +8,6 @@ COPY ["SerfBot/SerfBot.fsproj", "SerfBot/"] RUN dotnet restore "SerfBot/SerfBot.fsproj" COPY . . -COPY /home/root/appsettings.json /app/appsettings.json - WORKDIR "/src/SerfBot" RUN dotnet build "SerfBot.fsproj" -c Release -o /app/build @@ -19,4 +17,5 @@ RUN dotnet publish "SerfBot.fsproj" -c Release -o /app/publish FROM base AS final WORKDIR /app COPY --from=publish /app/publish . +COPY /tmp/serf-bot/appsettings.json ./appsettings.json ENTRYPOINT ["dotnet", "SerfBot.dll"] \ No newline at end of file diff --git a/SerfBot/appsettings.json b/SerfBot/appsettings.json index 5b32104..92583f4 100644 --- a/SerfBot/appsettings.json +++ b/SerfBot/appsettings.json @@ -1,5 +1,5 @@ { "UserIds": [], - "TelegramBotToken": "bot-token", - "OpenAiApiToken": "openai-token" + "TelegramBotToken": "270652017:AAG5O6FvMgMhB9MlOBsIF_yoKXaoiHD1GGs", + "OpenAiApiToken": "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" } \ No newline at end of file From 1776c68311ac360a9b3789b68f04dc34d58e910b Mon Sep 17 00:00:00 2001 From: boogiedk Date: Mon, 11 Dec 2023 14:37:56 +0300 Subject: [PATCH 27/35] fix --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 3c0ad51..e915f68 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,5 +17,5 @@ RUN dotnet publish "SerfBot.fsproj" -c Release -o /app/publish FROM base AS final WORKDIR /app COPY --from=publish /app/publish . -COPY /tmp/serf-bot/appsettings.json ./appsettings.json +COPY /opt/serf-bot/appsettings.json ./appsettings.json ENTRYPOINT ["dotnet", "SerfBot.dll"] \ No newline at end of file From a449ad431b11b7d3230e1eb6098cf423633aae88 Mon Sep 17 00:00:00 2001 From: boogiedk Date: Mon, 11 Dec 2023 14:46:41 +0300 Subject: [PATCH 28/35] fix --- Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index e915f68..bbdac7f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,5 +17,6 @@ RUN dotnet publish "SerfBot.fsproj" -c Release -o /app/publish FROM base AS final WORKDIR /app COPY --from=publish /app/publish . -COPY /opt/serf-bot/appsettings.json ./appsettings.json +COPY appsettings.json ./appsettings.json +COPY root/opt/serf-bot/appsettings.json ./appsettings.json ENTRYPOINT ["dotnet", "SerfBot.dll"] \ No newline at end of file From fa6b7fa12b042f6341a1eb51e77d33e53722c8b8 Mon Sep 17 00:00:00 2001 From: boogiedk Date: Mon, 11 Dec 2023 15:13:28 +0300 Subject: [PATCH 29/35] fix --- .github/workflows/docker-deploy.yml | 2 +- Dockerfile | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docker-deploy.yml b/.github/workflows/docker-deploy.yml index 48b28e8..04568cf 100644 --- a/.github/workflows/docker-deploy.yml +++ b/.github/workflows/docker-deploy.yml @@ -46,5 +46,5 @@ jobs: docker pull ${{ secrets.DOCKER_USERNAME }}/serf-bot:latest docker stop serf-bot || true docker rm serf-bot || true - docker run -d --name serf-bot -p 3005:3005 ${{ secrets.DOCKER_USERNAME }}/serf-bot:latest + docker run -d --name serf-bot -p 3005:3005 -v /tmp/serf-bot/:/app/ ${{ secrets.DOCKER_USERNAME }}/serf-bot:latest EOF diff --git a/Dockerfile b/Dockerfile index bbdac7f..558d78a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,6 +17,5 @@ RUN dotnet publish "SerfBot.fsproj" -c Release -o /app/publish FROM base AS final WORKDIR /app COPY --from=publish /app/publish . -COPY appsettings.json ./appsettings.json -COPY root/opt/serf-bot/appsettings.json ./appsettings.json +COPY /tmp/serf-bot/appsettings.json . ENTRYPOINT ["dotnet", "SerfBot.dll"] \ No newline at end of file From 31226844ab8c1a33a6817192f37e6c2a441d275a Mon Sep 17 00:00:00 2001 From: boogiedk Date: Mon, 11 Dec 2023 15:16:45 +0300 Subject: [PATCH 30/35] fix --- .github/workflows/docker-deploy.yml | 2 +- Dockerfile | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/docker-deploy.yml b/.github/workflows/docker-deploy.yml index 04568cf..0cb6a86 100644 --- a/.github/workflows/docker-deploy.yml +++ b/.github/workflows/docker-deploy.yml @@ -46,5 +46,5 @@ jobs: docker pull ${{ secrets.DOCKER_USERNAME }}/serf-bot:latest docker stop serf-bot || true docker rm serf-bot || true - docker run -d --name serf-bot -p 3005:3005 -v /tmp/serf-bot/:/app/ ${{ secrets.DOCKER_USERNAME }}/serf-bot:latest + docker run -d --name serf-bot -p 3005:3005-v /home/user/appsettings.json:/app/appsettings.json ${{ secrets.DOCKER_USERNAME }}/serf-bot:latest EOF diff --git a/Dockerfile b/Dockerfile index 558d78a..644279b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,5 +17,4 @@ RUN dotnet publish "SerfBot.fsproj" -c Release -o /app/publish FROM base AS final WORKDIR /app COPY --from=publish /app/publish . -COPY /tmp/serf-bot/appsettings.json . ENTRYPOINT ["dotnet", "SerfBot.dll"] \ No newline at end of file From b05cf3b7c95f9d78baf3c06f7d21863d708b0c5a Mon Sep 17 00:00:00 2001 From: boogiedk Date: Mon, 11 Dec 2023 15:19:12 +0300 Subject: [PATCH 31/35] fix --- .github/workflows/docker-deploy.yml | 2 +- SerfBot/appsettings.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docker-deploy.yml b/.github/workflows/docker-deploy.yml index 0cb6a86..a7c84e6 100644 --- a/.github/workflows/docker-deploy.yml +++ b/.github/workflows/docker-deploy.yml @@ -46,5 +46,5 @@ jobs: docker pull ${{ secrets.DOCKER_USERNAME }}/serf-bot:latest docker stop serf-bot || true docker rm serf-bot || true - docker run -d --name serf-bot -p 3005:3005-v /home/user/appsettings.json:/app/appsettings.json ${{ secrets.DOCKER_USERNAME }}/serf-bot:latest + docker run -d --name serf-bot -p 3005:3005 -v /home/user/appsettings.json:/app/appsettings.json ${{ secrets.DOCKER_USERNAME }}/serf-bot:latest EOF diff --git a/SerfBot/appsettings.json b/SerfBot/appsettings.json index 92583f4..6c14e2b 100644 --- a/SerfBot/appsettings.json +++ b/SerfBot/appsettings.json @@ -1,5 +1,5 @@ { "UserIds": [], - "TelegramBotToken": "270652017:AAG5O6FvMgMhB9MlOBsIF_yoKXaoiHD1GGs", - "OpenAiApiToken": "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" + "TelegramBotToken": "telegram-api-token", + "OpenAiApiToken": "open-ai-token" } \ No newline at end of file From 1b3488df9dba3c3cd70d3126fa6307cf23dd9a8c Mon Sep 17 00:00:00 2001 From: boogiedk Date: Mon, 11 Dec 2023 15:22:13 +0300 Subject: [PATCH 32/35] fix --- .github/workflows/docker-deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-deploy.yml b/.github/workflows/docker-deploy.yml index a7c84e6..5303877 100644 --- a/.github/workflows/docker-deploy.yml +++ b/.github/workflows/docker-deploy.yml @@ -46,5 +46,5 @@ jobs: docker pull ${{ secrets.DOCKER_USERNAME }}/serf-bot:latest docker stop serf-bot || true docker rm serf-bot || true - docker run -d --name serf-bot -p 3005:3005 -v /home/user/appsettings.json:/app/appsettings.json ${{ secrets.DOCKER_USERNAME }}/serf-bot:latest + docker run -d --name serf-bot -p 3005:3005 -v /tmp/serf-bot/appsettings.json:/app/appsettings.json ${{ secrets.DOCKER_USERNAME }}/serf-bot:latest EOF From 3dda3d1e6ecac7ae3c5ff818491574e29df75b71 Mon Sep 17 00:00:00 2001 From: boogiedk Date: Mon, 11 Dec 2023 15:31:05 +0300 Subject: [PATCH 33/35] fix --- SerfBot/TelegramBot.fs | 2 +- SerfBot/appsettings.json | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/SerfBot/TelegramBot.fs b/SerfBot/TelegramBot.fs index 3ab5542..a603f21 100644 --- a/SerfBot/TelegramBot.fs +++ b/SerfBot/TelegramBot.fs @@ -37,7 +37,7 @@ let streamToBase64 (stream: Stream) = let extractFileDataAsBase64 (fileResult: Result) = match fileResult with | Ok(file) -> - let filePath = Option.get file.FilePath // предполагается, что у вас есть свойство "Path" в типе "File" + let filePath = Option.get file.FilePath let apiUrl = $"https://api.telegram.org/file/bot{Configuration.config.TelegramBotToken}/{filePath}" use httpStream = new HttpClient() let f = httpStream.GetStreamAsync(apiUrl) |> Async.AwaitTask |> Async.RunSynchronously diff --git a/SerfBot/appsettings.json b/SerfBot/appsettings.json index 6c14e2b..97c4024 100644 --- a/SerfBot/appsettings.json +++ b/SerfBot/appsettings.json @@ -1,5 +1,7 @@ { - "UserIds": [], + "UserIds": [ + 123456 + ], "TelegramBotToken": "telegram-api-token", "OpenAiApiToken": "open-ai-token" } \ No newline at end of file From dfff8a367b66678d909a8bb2f89f8220a2070c28 Mon Sep 17 00:00:00 2001 From: boogiedk Date: Mon, 11 Dec 2023 15:39:33 +0300 Subject: [PATCH 34/35] fix --- SerfBot/OpenAiApi.fs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/SerfBot/OpenAiApi.fs b/SerfBot/OpenAiApi.fs index 72792ed..a0e2631 100644 --- a/SerfBot/OpenAiApi.fs +++ b/SerfBot/OpenAiApi.fs @@ -51,15 +51,19 @@ let gptAnswer userQuestion = let result = if completionResult.Successful then - completionResult.Choices |> Seq.head |> fun c -> c.Message.Content + completionResult.Choices + |> Seq.head + |> fun c -> c.Message.Content else match completionResult.Error with | null -> - "Unknown Error" |> logInfo - raise (Exception("Unknown Error")) + let errorMessage = "Unknown Error" + errorMessage |> logInfo + errorMessage | error -> - $"{error.Code} {error.Message}" |> logInfo - sprintf "%s: %s" error.Code error.Message + let errorMessage = $"{error.Code} {error.Message}" + errorMessage |> logInfo + errorMessage return result From 54631964a7b229567b4e44097cb465564ca81867 Mon Sep 17 00:00:00 2001 From: boogiedk Date: Mon, 11 Dec 2023 18:53:24 +0300 Subject: [PATCH 35/35] refactoring + fix workflow --- .github/workflows/docker-deploy.yml | 7 ++++- Dockerfile | 4 +-- SerfBot/Commands.fs | 19 ++++++++++-- SerfBot/Configuration.fs | 7 +++-- SerfBot/OpenAiApi.fs | 4 +-- SerfBot/Program.fs | 2 +- SerfBot/SerfBot.fsproj | 4 +-- SerfBot/TelegramApi.fs | 30 +++++++++++++++++++ SerfBot/TelegramBot.fs | 46 ++++++----------------------- SerfBot/Types.fs | 9 +++++- 10 files changed, 81 insertions(+), 51 deletions(-) diff --git a/.github/workflows/docker-deploy.yml b/.github/workflows/docker-deploy.yml index 5303877..ba6e431 100644 --- a/.github/workflows/docker-deploy.yml +++ b/.github/workflows/docker-deploy.yml @@ -3,7 +3,12 @@ name: CI/CD Pipline on: pull_request: branches: - - master + - master + types: + - closed + check_suite: + types: + - completed jobs: build: diff --git a/Dockerfile b/Dockerfile index 644279b..cb37364 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,8 @@ -FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base WORKDIR /app EXPOSE 80 -FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build WORKDIR /src COPY ["SerfBot/SerfBot.fsproj", "SerfBot/"] RUN dotnet restore "SerfBot/SerfBot.fsproj" diff --git a/SerfBot/Commands.fs b/SerfBot/Commands.fs index 938ad62..8c2c5ae 100644 --- a/SerfBot/Commands.fs +++ b/SerfBot/Commands.fs @@ -1,15 +1,26 @@ module SerfBot.Commands -open ExtCore.Control.Collections +open System open SerfBot.OpenAiApi; open SerfBot.Types +open SerfBot.Configuration + +let commandDescriptions = + "!ping - команда проверки связи\n" + + "!context - команда изменения контекста\n" + + "!vision - команда анализа присланной картинки\n" + + "!help - команда вывода списка команд\n" + + "!uptime - команда показа количества дней работы бота\n" + + "погода - команда получения погоды\n" + + "гпт - команда для вопроса в ChatGpt\n"; let commandHandler command = try match command with | Ping -> "pong" - | Vision (userText, base64Img) -> - descriptionAnalyzedImage userText base64Img + | Vision (userText, photo) -> + let base64img = TelegramApi.base64FromFileId (Array.last photo.Value).FileId + descriptionAnalyzedImage userText base64img |> Async.RunSynchronously | Context userText -> setupContext userText @@ -18,10 +29,12 @@ let commandHandler command = | Question userText -> gptAnswer userText |> Async.RunSynchronously + | Uptime -> $"Bot active is {(DateTime.Now.Date - startTime.Date).Days} days" | Weather city -> let weather = WeatherApi.getWeatherAsync city |> Async.RunSynchronously $"Погода в %s{city}: %s{weather}" + | HelpCommands -> commandDescriptions | _ -> "Некорректная команда" with | ex -> sprintf "Ошибка: %s" ex.Message \ No newline at end of file diff --git a/SerfBot/Configuration.fs b/SerfBot/Configuration.fs index a7cde30..b7f85a7 100644 --- a/SerfBot/Configuration.fs +++ b/SerfBot/Configuration.fs @@ -2,7 +2,8 @@ open Funogram.Telegram open Microsoft.Extensions.Configuration -open Types; +open Types +open System module Configuration = let mutable configBuilder = @@ -12,5 +13,7 @@ module Configuration = let config = configBuilder .Build() - .Get(); + .Get() + + let mutable startTime = DateTime.Now \ No newline at end of file diff --git a/SerfBot/OpenAiApi.fs b/SerfBot/OpenAiApi.fs index a0e2631..482edad 100644 --- a/SerfBot/OpenAiApi.fs +++ b/SerfBot/OpenAiApi.fs @@ -1,11 +1,11 @@ module SerfBot.OpenAiApi open OpenAI +open OpenAI.Chat open OpenAI.Managers open OpenAI.ObjectModels open OpenAI.ObjectModels.RequestModels open SerfBot.Types -open OpenAI.Chat open System open Log @@ -77,7 +77,7 @@ let descriptionAnalyzedImage userText base64Img = async { try let api = OpenAIClient(Configuration.config.OpenAiApiToken) - let userText2 = if userText == null then "Что на фото?" else userText + let userText2 = if String.IsNullOrEmpty(userText) then "Что на фото?" else userText let messages = [ Message(Role.System, Option.get currentContext) diff --git a/SerfBot/Program.fs b/SerfBot/Program.fs index a4c0f36..49047d9 100644 --- a/SerfBot/Program.fs +++ b/SerfBot/Program.fs @@ -1,5 +1,6 @@ namespace SerfBot +open System open Funogram.Api open Funogram.Telegram open Funogram.Telegram.Bot @@ -21,7 +22,6 @@ module Program = let telegramBotConfig = {Config.defaultConfig with Token = Configuration.config.TelegramBotToken } let! _ = Api.deleteWebhookBase () |> api telegramBotConfig - logInfo "SerfBot start" return! startBot telegramBotConfig updateArrivedMessage None } |> Async.RunSynchronously diff --git a/SerfBot/SerfBot.fsproj b/SerfBot/SerfBot.fsproj index 1b7cd93..e49d783 100644 --- a/SerfBot/SerfBot.fsproj +++ b/SerfBot/SerfBot.fsproj @@ -2,7 +2,7 @@ Exe - net7.0 + net8.0 @@ -11,8 +11,8 @@ - + diff --git a/SerfBot/TelegramApi.fs b/SerfBot/TelegramApi.fs index 67a5261..3152296 100644 --- a/SerfBot/TelegramApi.fs +++ b/SerfBot/TelegramApi.fs @@ -2,6 +2,13 @@ open Funogram.Telegram open Funogram.Telegram.Types +open System +open System.IO +open System.Net.Http +open Funogram.Api +open Funogram.Telegram.Bot +open SerfBot.Types +open ExtCore.Control.Collections let public sendMessageFormatted text parseMode config bot chatId = Req.SendMessage.Make(ChatId.Int chatId, text, parseMode = parseMode) |> bot config @@ -9,3 +16,26 @@ let public sendMessageFormatted text parseMode config bot chatId = let public sendReplayMessageFormatted text parseMode config bot chatId replyToMessageId = Req.SendMessage.Make(ChatId.Int chatId, text, replyToMessageId = replyToMessageId, parseMode = parseMode) |> bot config +let streamToBase64 (stream: Stream) = + use ms = new MemoryStream() + stream.CopyTo(ms) + let buffer = ms.ToArray() + Convert.ToBase64String(buffer) + +let extractBase64File fileResult = + match fileResult with + | Ok file -> + let filePath = Option.get file.FilePath + let apiUrl = $"https://api.telegram.org/file/bot{Configuration.config.TelegramBotToken}/{filePath}" + use httpStream = new HttpClient() + httpStream.GetStreamAsync(apiUrl) + |> Async.AwaitTask + |> Async.RunSynchronously + |> streamToBase64 + | _ -> failwith "Error when getting file" + +let base64FromFileId fileId = + Req.GetFile.Make fileId + |> api {Config.defaultConfig with Token = Configuration.config.TelegramBotToken } + |> Async.RunSynchronously + |> extractBase64File \ No newline at end of file diff --git a/SerfBot/TelegramBot.fs b/SerfBot/TelegramBot.fs index a603f21..8d7e1a5 100644 --- a/SerfBot/TelegramBot.fs +++ b/SerfBot/TelegramBot.fs @@ -1,11 +1,7 @@ module SerfBot.TelegramBot open System -open System.IO -open System.Net.Http -open ExtCore.Control.Collections open Funogram.Api -open Funogram.Telegram open Funogram.Telegram.Bot open Funogram.Telegram.Types open SerfBot.Log @@ -26,39 +22,12 @@ let isValidUser (userId: int64) = let processCommand (ctx: UpdateContext, command: MessageReplayCommand) = sendReplayMessageFormatted command.ReplayText ParseMode.Markdown ctx.Config api command.Chat.Id command.MessageId |> Async.RunSynchronously - |> ignore - -let streamToBase64 (stream: Stream) = - use ms = new MemoryStream() - stream.CopyTo(ms) - let buffer = ms.ToArray() - Convert.ToBase64String(buffer) - -let extractFileDataAsBase64 (fileResult: Result) = - match fileResult with - | Ok(file) -> - let filePath = Option.get file.FilePath - let apiUrl = $"https://api.telegram.org/file/bot{Configuration.config.TelegramBotToken}/{filePath}" - use httpStream = new HttpClient() - let f = httpStream.GetStreamAsync(apiUrl) |> Async.AwaitTask |> Async.RunSynchronously - let base64 = streamToBase64 f - base64 - - - -let handleFiles fileId ctx = - let file = Req.GetFile.Make fileId - |> api ctx.Config - |> Async.RunSynchronously - let base64Img = extractFileDataAsBase64 file - base64Img - + |> ignore let updateArrivedMessage (ctx: UpdateContext) = match ctx.Update.Message with - | Some { MessageId = messageId; Chat = chat; Text = text; Photo = photo; Caption = caption } -> - let user = ctx.Update.Message.Value.From.Value - let base64Img = if photo.IsSome then handleFiles (Array.last photo.Value).FileId ctx else "" + | Some { MessageId = messageId; Chat = chat; Text = text; Photo = photo; Caption = caption; From = from } -> + let user = from.Value; let message = if text.IsSome then text.Value elif caption.IsSome then caption.Value else "" match isValidUser user.Id with | Some () -> @@ -67,14 +36,17 @@ let updateArrivedMessage (ctx: UpdateContext) = let commandType = match command with | "!ping" -> Ping - | "погода" -> Weather userMessage | "!context" -> Context userMessage - | "!vision" -> Vision (userMessage, base64Img) + | "!vision" -> Vision (userMessage, photo) + | "!help" -> HelpCommands + | "!uptime" -> Uptime + | "погода" -> Weather userMessage | "гпт" -> Question userMessage | _ -> Other userMessage let replyText = Commands.commandHandler commandType processCommand(ctx, { Chat = chat; MessageId = messageId; Text = text; ReplayText = replyText }) + | None -> sprintf "Authorize error." |> logInfo | _ -> () - | None -> sprintf "Authorize error." |> logInfo + | None -> sprintf "Error." |> logInfo | _ -> () \ No newline at end of file diff --git a/SerfBot/Types.fs b/SerfBot/Types.fs index 9d56f26..40e2aac 100644 --- a/SerfBot/Types.fs +++ b/SerfBot/Types.fs @@ -1,6 +1,10 @@ module SerfBot.Types +open System open Funogram.Telegram +open Funogram.Telegram.Types + +let mutable internal startTime = DateTime type CityCoordinates = { CityName: string @@ -29,13 +33,16 @@ type ApplicationConfiguration = { TelegramBotToken: string OpenAiApiToken: string UserIds: int64[] + StarBotDatetime: DateTime } type Command = | Ping | Question of string | Context of string - | Vision of string * string + | Vision of string * PhotoSize array option | Weather of string + | Uptime + | HelpCommands | Other of string