Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add MarkdownV2 support #75

Merged
merged 1 commit into from
Apr 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/Funogram.Generator/Types/TypesGenerator.fs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ type ParseMode =
| Markdown
/// Html parse syntax
| HTML
/// MarkdownV2 parse syntax
| [<DataMember(Name = "MarkdownV2")>] MarkdownV2

/// Type of action to broadcast
type ChatAction =
Expand Down
2 changes: 1 addition & 1 deletion src/Funogram.Telegram/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>7.2.0</Version>
<Version>7.2.0.1</Version>
<Authors>Nikolay Matyushin</Authors>
<Product>Funogram.Telegram</Product>
<Title>Funogram.Telegram</Title>
Expand Down
2 changes: 2 additions & 0 deletions src/Funogram.Telegram/Types.fs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ type ParseMode =
| Markdown
/// Html parse syntax
| HTML
/// MarkdownV2 parse syntax
| [<DataMember(Name = "MarkdownV2")>] MarkdownV2

/// Type of action to broadcast
type ChatAction =
Expand Down
3 changes: 3 additions & 0 deletions src/Funogram.Tests/Constants.fs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ module Constants =
}: Req.ForwardMessage) :> IBotRequest
let jsonForwardMessageReq = """{"chat_id":"Dolfik","from_chat_id":10,"message_id":10}"""

let sendMessageReq = Req.SendMessage.Make(ChatId.String "Dolfik", "Hello, world", parseMode = ParseMode.MarkdownV2) :> IBotRequest
let jsonSendMessageReq = """{"chat_id":"Dolfik","text":"Hello, world","parse_mode":"MarkdownV2"}"""

let jsonTestObjChatMember = ChatMemberMember.Create("member", User.Create(600000000L, false, "firstName", "lastName", "userName", "ru"))
let jsonTestObjChatMemberResultString = """{"ok":true,"result":{"status":"member","user":{"id":600000000,"is_bot":false,"first_name":"firstName","last_name":"lastName","username":"userName","language_code":"ru"}}}"""

8 changes: 7 additions & 1 deletion src/Funogram.Tests/Json.fs
Original file line number Diff line number Diff line change
Expand Up @@ -115,4 +115,10 @@ let ``JSON deserializing ChatMember``() =
|> parseJson
|> function
| Ok result -> shouldEqual result Constants.jsonTestObjChatMember
| Error error -> failwith error.Description
| Error error -> failwith error.Description

[<Fact>]
let ``JSON serializing send message request`` () =
Constants.sendMessageReq
|> toJsonBotRequestString
|> shouldEqual Constants.jsonSendMessageReq
15 changes: 2 additions & 13 deletions src/Funogram/Converters.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ open System
open System.Collections.Generic
open System.IO
open System.Runtime.CompilerServices
open System.Runtime.Serialization
open System.Text.Json
open System.Text.Json.Serialization
open TypeShape.Core
Expand All @@ -25,8 +24,8 @@ module internal Converters =
&& (case.Fields[1].Member.Type = typeof<Stream> || case.Fields[1].Member.Type = typeof<byte[]>)

if case.Fields.Length = 0 then
let name = caseName case.CaseInfo
fun (writer: Utf8JsonWriter) _ _ ->
let name = toSnakeCase case.CaseInfo.Name
writer.WriteStringValue(name)
else
case.Fields[0].Accept { new IMemberVisitor<'DeclaringType, Utf8JsonWriter -> 'DeclaringType -> JsonSerializerOptions -> unit> with
Expand Down Expand Up @@ -89,17 +88,7 @@ module internal Converters =
union.UnionCases
|> Seq.map (fun c ->
if c.Fields.Length = 0 then
let dataMember =
c.CaseInfo.GetCustomAttributes(typeof<DataMemberAttribute>)
|> Seq.cast<DataMemberAttribute>
|> Seq.filter (fun x -> String.IsNullOrEmpty(x.Name) |> not)
|> Seq.toArray

let name =
if dataMember.Length > 0
then dataMember[0].Name
else c.CaseInfo.Name |> toSnakeCase
(Set.ofList [name], None)
(Set.ofList [caseName c.CaseInfo], None)
else
let tp = c.Fields[0].Member.Type
if tp.IsPrimitive then (Set.empty, Some tp)
Expand Down
2 changes: 1 addition & 1 deletion src/Funogram/Funogram.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<VersionPrefix>3.0.0</VersionPrefix>
<VersionPrefix>3.0.1</VersionPrefix>
<Authors>Nikolay Matyushin</Authors>
<Product>Funogram</Product>
<Description>Funogram is a functional Telegram Bot Api library for F#</Description>
Expand Down
15 changes: 14 additions & 1 deletion src/Funogram/StringUtils.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ module Funogram.StringUtils
open System
open System.Linq.Expressions
open System.Reflection
open System.Runtime.Serialization
open Microsoft.FSharp.Reflection

let toSnakeCase =
let assembly = Assembly.Load("System.Text.Json")
Expand All @@ -21,4 +23,15 @@ let toSnakeCase =

// not good solution, but fastest
let fn = createConvertNameFunc instance
fn.Invoke
fn.Invoke

let inline caseName (caseInfo: UnionCaseInfo) =
let dataMember =
caseInfo.GetCustomAttributes(typeof<DataMemberAttribute>)
|> Seq.cast<DataMemberAttribute>
|> Seq.filter (fun x -> String.IsNullOrEmpty(x.Name) |> not)
|> Seq.toArray

if dataMember.Length > 0
then dataMember[0].Name
else caseInfo.Name |> toSnakeCase
4 changes: 2 additions & 2 deletions src/Funogram/Tools.fs
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ module Api =
else None

if isEnum then
let name = StringUtils.toSnakeCase case.CaseInfo.Name
let name = StringUtils.caseName case.CaseInfo
fun _ (prop: string) (data: MultipartFormDataContent) ->
data.Add(strf "%s" name, prop) $ true
else
Expand Down Expand Up @@ -422,7 +422,7 @@ module Api =

use content = new MultipartFormDataContent()
let hasData = serialize request content

let! result =
if hasData then client.PostAsync(url, content) |> Async.AwaitTask
else client.GetAsync(url) |> Async.AwaitTask
Expand Down
2 changes: 2 additions & 0 deletions src/examples/Funogram.TestBot/Commands/Base.fs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ let defaultText = """⭐️Available test commands:
/send_message7 - Test inline keyboard
/send_message8 - Test multiple media
/send_message9 - Test multiple media as bytes
/send_message10 - MarkdownV2 test

/send_action - Test action

Expand Down Expand Up @@ -46,6 +47,7 @@ let updateArrived (ctx: UpdateContext) =

cmd "/send_message8" (fun _ -> Files.testUploadAndSendPhotoGroup |> wrap)
cmd "/send_message9" (fun _ -> Files.testUploadAndSendPhotoGroupAsBytes |> wrap)
cmd "/send_message10" (fun _ -> TextMessages.testMarkdownV2 |> wrap)

cmd "/forward_message" (fun _ -> TextMessages.testForwardMessage ctx |> wrap)

Expand Down
58 changes: 56 additions & 2 deletions src/examples/Funogram.TestBot/Commands/TextMessages.fs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,62 @@ open Funogram.Telegram.Types
let private sendMessageFormatted text parseMode config chatId =
Req.SendMessage.Make(ChatId.Int chatId, text, parseMode = parseMode) |> bot config

let testMarkdown = sendMessageFormatted "Test *Markdown*" ParseMode.Markdown
let testHtml = sendMessageFormatted "Test <b>HTML</b>" ParseMode.HTML
[<Literal>]
let MarkdownExample = """*bold text*
_italic text_
[inline URL](http://www.example.com/)
[inline mention of a user](tg://user?id=123456789)
`inline fixed-width code`
```
pre-formatted fixed-width code block
```
```python
pre-formatted fixed-width code block written in the Python programming language
```"""

[<Literal>]
let MarkdownV2Example = """*bold \*text*
_italic \*text_
__underline__
~strikethrough~
||spoiler||
*bold _italic bold ~italic bold strikethrough ||italic bold strikethrough spoiler||~ __underline italic bold___ bold*
[inline URL](http://www.example.com/)
[inline mention of a user](tg://user?id=123456789)
![👍](tg://emoji?id=5368324170671202286)
`inline fixed-width code`
```
pre-formatted fixed-width code block
```
```python
pre-formatted fixed-width code block written in the Python programming language
```
>Block quotation started
>Block quotation continued
>The last line of the block quotation**
>The second block quotation started right after the previous\r
>The third block quotation started right after the previous"""

[<Literal>]
let HtmlExample = """
<b>bold</b>, <strong>bold</strong>
<i>italic</i>, <em>italic</em>
<u>underline</u>, <ins>underline</ins>
<s>strikethrough</s>, <strike>strikethrough</strike>, <del>strikethrough</del>
<span class="tg-spoiler">spoiler</span>, <tg-spoiler>spoiler</tg-spoiler>
<b>bold <i>italic bold <s>italic bold strikethrough <span class="tg-spoiler">italic bold strikethrough spoiler</span></s> <u>underline italic bold</u></i> bold</b>
<a href="http://www.example.com/">inline URL</a>
<a href="tg://user?id=123456789">inline mention of a user</a>
<tg-emoji emoji-id="5368324170671202286">👍</tg-emoji>
<code>inline fixed-width code</code>
<pre>pre-formatted fixed-width code block</pre>
<pre><code class="language-python">pre-formatted fixed-width code block written in the Python programming language</code></pre>
<blockquote>Block quotation started\nBlock quotation continued\nThe last line of the block quotation</blockquote>
"""

let testMarkdown = sendMessageFormatted MarkdownExample ParseMode.Markdown
let testMarkdownV2 = sendMessageFormatted MarkdownV2Example ParseMode.MarkdownV2
let testHtml = sendMessageFormatted HtmlExample ParseMode.HTML
let testNoWebpageAndNotification config chatId =
Req.SendMessage.Make(
ChatId.Int chatId,
Expand Down
Loading