X-Git-Url: http://git.giacomolli.uk/onechat/blobdiff_plain/d206de3a2e839ffbda9cbdb10a3ed93c4915fce3..a345df4e4bbf9dbaf3da1ce548896eef8f515de3:/botapi.go?ds=sidebyside diff --git a/botapi.go b/botapi.go index 9086fdc..d02aff0 100644 --- a/botapi.go +++ b/botapi.go @@ -21,6 +21,13 @@ const ( // 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 ) @@ -30,11 +37,13 @@ const ( 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 @@ -86,9 +95,10 @@ type botUser struct { // 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, } } @@ -109,16 +119,24 @@ func (b *botAPI) GetUpdates(ctx context.Context, offset int64) ([]botUpdate, err 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",