diff --git a/src/Botinok.Common/Botinok.Common.fsproj b/src/Botinok.Common/Botinok.Common.fsproj index e80975b..fe5d2d6 100644 --- a/src/Botinok.Common/Botinok.Common.fsproj +++ b/src/Botinok.Common/Botinok.Common.fsproj @@ -1,7 +1,7 @@  - net6.0 + net8.0 true diff --git a/src/Botinok.Libgen/Botinok.Libgen.fsproj b/src/Botinok.Libgen/Botinok.Libgen.fsproj index ae55a75..e29f5a0 100644 --- a/src/Botinok.Libgen/Botinok.Libgen.fsproj +++ b/src/Botinok.Libgen/Botinok.Libgen.fsproj @@ -1,7 +1,7 @@  - net7.0 + net8.0 true diff --git a/src/Botinok.Libgen/Parser.fs b/src/Botinok.Libgen/Parser.fs index 40aec6a..44b0a0c 100644 --- a/src/Botinok.Libgen/Parser.fs +++ b/src/Botinok.Libgen/Parser.fs @@ -3,12 +3,9 @@ open FSharp.Data open Botinok.Libgen.Types -[] -let private newLine = "\r\n" - let private getId (nodes: HtmlNode) = nodes.TryGetAttribute("href") - |> Option.map (fun x -> x.Value()) + |> Option.map (_.Value()) |> Option.defaultValue "" |> fun x -> x.Split('=') |> Seq.last @@ -25,29 +22,32 @@ let extractBooks content = let name = x[2].Descendants["a"] |> Seq.head - |> fun x -> x.Elements() |> Seq.head |> (fun x -> x.InnerText()) + |> fun x -> x.Elements() |> Seq.head |> (_.InnerText()) - let isbn = x[2].Descendants() |> Seq.last |> (fun x -> x.InnerText()) + let isbn = x[2].Descendants() |> Seq.last |> (_.InnerText()) let edition = x[2].Descendants["font"] |> Seq.toList |> fun nodes -> if nodes.Length = 3 then nodes |> List.skip 1 else nodes |> List.tryHead - |> Option.map (fun node -> node.InnerText()) + |> Option.map (_.InnerText()) |> Option.defaultValue "" |> fun text -> if (text = isbn || text = name) then "" else text - - { ISBN = isbn - Authors = + + let authors = x[1].Descendants() |> Seq.toList |> List.filter (fun node -> match node with - | HtmlText text -> text <> newLine + | HtmlText text -> text <> "\r\n" | _ -> false) - |> List.map (fun x -> x.InnerText()) + |> List.map (_.InnerText()) + + let id = x[2].Descendants["a"] |> Seq.last |> getId + { ISBN = isbn + Authors = authors Name = name Publisher = x[3].InnerText() Edition = edition @@ -56,18 +56,18 @@ let extractBooks content = Language = x[6].InnerText() Size = x[7].InnerText() Extension = x[8].InnerText() - Id = x[2].Descendants["a"] |> Seq.last |> getId }) + Id = id }) let extractLinksFromDownloadPage content = HtmlDocument.Parse(content).Descendants["a"] - |> Seq.choose (fun x -> x.TryGetAttribute("href") |> Option.map (fun a -> a.Value())) + |> Seq.choose (fun x -> x.TryGetAttribute("href") |> Option.map (_.Value())) |> Seq.take 4 let extractBook content = let trs = HtmlDocument.Parse(content).Descendants["table"] |> Seq.head - |> fun x -> x.Elements() + |> _.Elements() { ISBN = (trs[7].Elements()[1]).InnerText() Authors = (trs[2].Elements()[1]).InnerText().Split(',') |> Seq.toList diff --git a/src/Botinok.Libgen/Types.fs b/src/Botinok.Libgen/Types.fs index aab5109..1422a47 100644 --- a/src/Botinok.Libgen/Types.fs +++ b/src/Botinok.Libgen/Types.fs @@ -2,7 +2,6 @@ open System.Web - type Genre = | NonFiction | Fiction diff --git a/src/Botinok/Botinok.fsproj b/src/Botinok/Botinok.fsproj index 11b1abc..6f53d9a 100644 --- a/src/Botinok/Botinok.fsproj +++ b/src/Botinok/Botinok.fsproj @@ -2,11 +2,12 @@ Exe - net7.0 + net8.0 Linux + @@ -22,8 +23,8 @@ - - + + diff --git a/src/Botinok/Commands/Base.fs b/src/Botinok/Commands/Base.fs index 558a98e..a5f1548 100644 --- a/src/Botinok/Commands/Base.fs +++ b/src/Botinok/Commands/Base.fs @@ -17,7 +17,7 @@ let wrap ctx fn = let fromId () = match ctx.Update.Message with | Some msg -> msg.From.Value.Id - | None _ -> ctx.Update.CallbackQuery.Value.Message.Value.Chat.Id + | _ -> ctx.Update.CallbackQuery.Value.From.Id fn ctx.Config (fromId()) let updateArrived (ctx: UpdateContext) = diff --git a/src/Botinok/Commands/Libgen.fs b/src/Botinok/Commands/Libgen.fs index a62b058..61d5bf3 100644 --- a/src/Botinok/Commands/Libgen.fs +++ b/src/Botinok/Commands/Libgen.fs @@ -1,8 +1,8 @@ module Botinok.Commands.Libgen +open Botinok open Botinok.Libgen.Types open Botinok.LibgenClient -open Funogram.Telegram open Funogram.Telegram.Bot open Funogram.Telegram.Types open Botinok.Core @@ -32,7 +32,7 @@ let private bookListMarkup (books: Book seq) book page = keyboard -let private bookInfoTemplate (book: Book) = +let private bookInfoMarkup (book: Book) = let markup = Markup.InlineKeyboardMarkup { InlineKeyboard = [| [| InlineKeyboardButton.Create("скачать", callbackData = $"book/download/{book.Id}") @@ -40,14 +40,12 @@ let private bookInfoTemplate (book: Book) = |] } (book.ToString(), markup) - -let private callbackMessageId (ctx: UpdateContext) = ctx.Update.CallbackQuery.Value.Message.Value.MessageId -let private commandMessageId (ctx: UpdateContext) = ctx.Update.Message.Value.MessageId let private libgenClient = LibgenClient() - + let searchBooks (book: string) (ctx: UpdateContext) config (chatId: int64) = - if book.Length < 3 then Req.SendMessage.Make(chatId, "длина запроса должна быть больше 2 символов", replyToMessageId = commandMessageId ctx) |> bot config + if book.Length < 3 then + Requests.sendReply chatId "длина запроса должна быть больше 2 символов" ctx |> bot config let booksResult = libgenClient.Search(SearchQuery.createDefaultQuery(book, 1)) @@ -55,42 +53,29 @@ let searchBooks (book: string) (ctx: UpdateContext) config (chatId: int64) = |> Async.RunSynchronously match booksResult with - | Ok books -> - Req.SendMessage.Make( - chatId, - $"\"{book}\"", - replyMarkup = Markup.InlineKeyboardMarkup { InlineKeyboard = bookListMarkup books book 1 }, - replyToMessageId = commandMessageId ctx - ) - |> bot config - | Error error -> - Req.SendMessage.Make(chatId, error, replyToMessageId = commandMessageId ctx) - |> bot config + | Ok books -> Requests.sendReplyMarkup + chatId + $"\"{book}\"" + ctx + (Markup.InlineKeyboardMarkup { InlineKeyboard = bookListMarkup books book 1 }) + |> bot config + | Error error -> Requests.sendReply chatId error ctx |> bot config let downloadBook id (ctx: UpdateContext) config (chatId: int64) = - let bookResult = - libgenClient.DownloadBook id |> Async.AwaitTask |> Async.RunSynchronously + let bookResult = libgenClient.DownloadBook id |> Async.AwaitTask |> Async.RunSynchronously match bookResult with - | Ok(file, name) -> - Req.SendDocument.Make(chatId, InputFile.File(name, file), replyToMessageId = callbackMessageId ctx) - |> bot config - | Error error -> - Req.SendMessage.Make(chatId, error, replyToMessageId = callbackMessageId ctx) - |> bot config + | Ok(file, name) -> Requests.sendDocument chatId name file ctx |> bot config + | Error error -> Requests.sendReply chatId error ctx |> bot config let getBook id (ctx: UpdateContext) config (chatId: int64) = - let bookResult = - libgenClient.GetBook id |> Async.AwaitTask |> Async.RunSynchronously + let bookResult = libgenClient.GetBook id |> Async.AwaitTask |> Async.RunSynchronously match bookResult with | Ok book -> - let info, markup = bookInfoTemplate book - Req.SendMessage.Make(chatId, info, replyMarkup = markup, replyToMessageId = callbackMessageId ctx) - |> bot config - | Error error -> - Req.SendMessage.Make(chatId, error, replyToMessageId = callbackMessageId ctx) - |> bot config + let info, markup = bookInfoMarkup book + Requests.sendReplyMarkup chatId info ctx markup |> bot config + | Error error -> Requests.sendReply chatId error ctx |> bot config let getAnotherPages book (page: string) (ctx: UpdateContext) config (chatId: int64) = let page = int page @@ -101,13 +86,10 @@ let getAnotherPages book (page: string) (ctx: UpdateContext) config (chatId: int |> Async.RunSynchronously match booksResult with - | Ok books -> - Req.EditMessageReplyMarkup.Make( - chatId = chatId, - messageId = callbackMessageId ctx, - replyMarkup = InlineKeyboardMarkup.Create(bookListMarkup books book page) - ) - |> bot config - | Error error -> - Req.SendMessage.Make(chatId = chatId, text = error, replyToMessageId = callbackMessageId ctx) - |> bot config \ No newline at end of file + | Ok books -> Requests.editMessageReplyMarkup + chatId + ctx + (InlineKeyboardMarkup.Create(bookListMarkup books book page)) + |> bot config + | Error error -> Requests.sendReply chatId error ctx |> bot config + \ No newline at end of file diff --git a/src/Botinok/Program.fs b/src/Botinok/Program.fs index c469fba..22da69f 100644 --- a/src/Botinok/Program.fs +++ b/src/Botinok/Program.fs @@ -4,6 +4,7 @@ open Funogram.Telegram.Bot open Botinok open Funogram.Api open Funogram.Telegram +open Funogram.Telegram.Types open Microsoft.Extensions.Configuration open Serilog @@ -31,6 +32,7 @@ let main _ = |> logInfo let! _ = Api.deleteWebhookBase () |> api botConfig + return! startBot botConfig Commands.Base.updateArrived None } |> Async.RunSynchronously diff --git a/src/Botinok/Requests.fs b/src/Botinok/Requests.fs new file mode 100644 index 0000000..c4b2355 --- /dev/null +++ b/src/Botinok/Requests.fs @@ -0,0 +1,25 @@ +module Botinok.Requests + +open Funogram.Telegram +open Funogram.Telegram.Bot +open Funogram.Telegram.Types + +let private getMessageId (ctx: UpdateContext) = + match ctx.Update.Message with + | Some msg -> msg.MessageId + | _ -> + match ctx.Update.CallbackQuery.Value.Message.Value with + | Message m -> m.MessageId + | InaccessibleMessage im -> im.MessageId + +let sendReply (chatId: int64) msg ctx = + Req.SendMessage.Make(chatId, msg, replyParameters = ReplyParameters.Create(messageId = getMessageId ctx)) + +let sendReplyMarkup (chatId: int64) msg ctx markup = + Req.SendMessage.Make(chatId, msg, replyParameters = ReplyParameters.Create(messageId = getMessageId ctx), replyMarkup = markup) + +let editMessageReplyMarkup (chatId: int64) (ctx: UpdateContext) markup = + Req.EditMessageReplyMarkup.Make(chatId = ChatId.Int chatId, messageId = getMessageId ctx, replyMarkup = markup) + +let sendDocument (chatId: int64) filename file ctx = + Req.SendDocument.Make(chatId, InputFile.File(filename, file), replyParameters = ReplyParameters.Create(messageId = getMessageId ctx)) \ No newline at end of file