// take before the bot gives it up.
pollSlack = 20 * time.Second
+ // sendTimeout is how long a sendMessage or sendChatAction call can
+ // take. A connection that stops without a close, such as after a
+ // network change, then fails the call, and the sender tries again,
+ // instead of a wait until TCP gives up. The client has no timeout of
+ // its own, because the long poll of getUpdates needs a longer limit.
+ sendTimeout = 30 * time.Second
+
// botAnswerBytes is the most bytes of one answer of the Bot API.
botAnswerBytes = 16 << 20
)
var errBotNetwork = errors.New("no valid answer from the Bot API")
// botAPI calls the Telegram Bot API. The bot token is a part of each URL, so no
-// error of botAPI holds a URL, a net/http error, or the token.
+// error of botAPI holds a URL, a net/http error, or the token. SendTimeout
+// limits each sendMessage and sendChatAction call.
type botAPI struct {
- base string
- token string
- http *http.Client
+ base string
+ token string
+ http *http.Client
+ sendTimeout time.Duration
}
// botError is an answer of the Bot API that tells of a failure. RetryAfter is
Type string `json:"type"`
}
+// botEntity is a message entity of sendMessage: a part of the text with a
+// format, such as bold or a link. Offset and Length count UTF-16 code units.
+// URL is the target of a text_link, and Language the language of a pre.
+type botEntity struct {
+ Type string `json:"type"`
+ Offset int `json:"offset"`
+ Length int `json:"length"`
+ URL string `json:"url,omitempty"`
+ Language string `json:"language,omitempty"`
+}
+
// botUser is the sender of a message.
type botUser struct {
ID int64 `json:"id"`
// newBotAPI returns the client of the Bot API at base with token.
func newBotAPI(base, token string) *botAPI {
return &botAPI{
- base: base,
- token: token,
- http: &http.Client{},
+ base: base,
+ token: token,
+ http: &http.Client{},
+ sendTimeout: sendTimeout,
}
}
return updates, err
}
-// SendMessage sends text to chat as plain text, with no parse mode.
+// SendMessage sends text to chat as plain text, with no parse mode. A call that
+// takes longer than sendTimeout fails with errBotNetwork.
func (b *botAPI) SendMessage(ctx context.Context, chat int64, text string) error {
+ ctx, cancel := context.WithTimeout(ctx, b.sendTimeout)
+ defer cancel()
+
return b.call(ctx, "sendMessage", map[string]any{
"chat_id": chat,
"text": text,
}, nil)
}
-// SendTyping shows the typing action in chat for about five seconds.
+// SendTyping shows the typing action in chat for about five seconds. A call
+// that takes longer than sendTimeout fails with errBotNetwork.
func (b *botAPI) SendTyping(ctx context.Context, chat int64) error {
+ ctx, cancel := context.WithTimeout(ctx, b.sendTimeout)
+ defer cancel()
+
return b.call(ctx, "sendChatAction", map[string]any{
"chat_id": chat,
"action": "typing",