-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrich.go
More file actions
121 lines (110 loc) · 3.86 KB
/
Copy pathrich.go
File metadata and controls
121 lines (110 loc) · 3.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
// SPDX-License-Identifier: Apache-2.0
package tg
import "context"
// Rich messages (Bot API 10.1+) carry markup Telegram's classic parse_mode
// cannot express and are not capped at 4096 characters. Exactly one of html,
// markdown or blocks may be set.
//
// Entity detection is off in both builders: a transcript or a commit message
// full of phone numbers, hashtags and card-like digits should not turn into a
// field of links.
// RichOption adjusts how Telegram reads a rich payload.
type RichOption func(map[string]any)
// WithEntityDetection lets Telegram find links, hashtags, phone numbers and
// mentions in the payload itself. It is off by default because generated text
// -- a transcript, a diff, a commit message -- is full of digits and words
// that are not any of those. Turn it on for text a person wrote, or for
// markdown whose author expects a bare URL to become a link.
func WithEntityDetection() RichOption {
return func(rich map[string]any) { delete(rich, "skip_entity_detection") }
}
func richHTML(body string, opts ...RichOption) map[string]any {
return applyRichOptions(map[string]any{
"html": body,
"skip_entity_detection": true,
}, opts)
}
// richMarkdown parses GFM. Prefer [richHTML] for anything generated: markdown
// would additionally interpret *, _, #, |, backticks and list-like lines that
// happen to appear in the payload.
func richMarkdown(body string, opts ...RichOption) map[string]any {
return applyRichOptions(map[string]any{
"markdown": body,
"skip_entity_detection": true,
}, opts)
}
func applyRichOptions(rich map[string]any, opts []RichOption) map[string]any {
for _, opt := range opts {
if opt != nil {
opt(rich)
}
}
return rich
}
// SendRichHTML sends one rich message, optionally as a reply and inside a
// forum topic.
func (c *Client) SendRichHTML(ctx context.Context, chatID, replyTo int64, threadID int, body string, markup *InlineKeyboardMarkup, opts ...RichOption) (Message, error) {
req := map[string]any{
"chat_id": chatID,
"rich_message": richHTML(body, opts...),
}
if replyTo > 0 {
req["reply_parameters"] = map[string]any{
"message_id": replyTo,
"allow_sending_without_reply": true,
}
}
if threadID > 0 {
req["message_thread_id"] = threadID
}
if markup != nil {
req["reply_markup"] = markup
}
return c.sendRichMessage(ctx, req)
}
// SendRichMarkdown sends GFM. Only for content authored as markdown by a
// person or a source that owns its own escaping.
func (c *Client) SendRichMarkdown(ctx context.Context, chatID int64, markdown string, markup *InlineKeyboardMarkup, opts ...RichOption) (Message, error) {
req := map[string]any{
"chat_id": chatID,
"rich_message": richMarkdown(markdown, opts...),
}
if markup != nil {
req["reply_markup"] = markup
}
return c.sendRichMessage(ctx, req)
}
// EditMessageRichHTML replaces an ordinary message with rich content. It is
// what turns a "working…" placeholder into the finished answer, so the user
// watches one message instead of waiting on an empty screen.
func (c *Client) EditMessageRichHTML(ctx context.Context, chatID, messageID int64, body string, markup *InlineKeyboardMarkup, opts ...RichOption) error {
req := map[string]any{
"chat_id": chatID,
"message_id": messageID,
"rich_message": richHTML(body, opts...),
}
if markup != nil {
req["reply_markup"] = markup
}
var resp struct {
OK bool `json:"ok"`
}
err := c.post(ctx, "editMessageText", req, &resp)
if IsMessageNotModified(err) {
return nil
}
return err
}
func (c *Client) sendRichMessage(ctx context.Context, req map[string]any) (Message, error) {
var resp struct {
OK bool `json:"ok"`
Result Message `json:"result"`
}
if err := c.post(ctx, "sendRichMessage", req, &resp); err != nil {
return Message{}, err
}
if !resp.OK {
return Message{}, okFalse("sendRichMessage")
}
return resp.Result, nil
}