aboutsummaryrefslogtreecommitdiff
path: root/internal/integration/telegrambot/client.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/integration/telegrambot/client.go')
-rw-r--r--internal/integration/telegrambot/client.go174
1 files changed, 174 insertions, 0 deletions
diff --git a/internal/integration/telegrambot/client.go b/internal/integration/telegrambot/client.go
new file mode 100644
index 00000000..ba7cedd2
--- /dev/null
+++ b/internal/integration/telegrambot/client.go
@@ -0,0 +1,174 @@
+// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package telegrambot // import "miniflux.app/v2/internal/integration/telegrambot"
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "net/url"
+ "time"
+
+ "miniflux.app/v2/internal/version"
+)
+
+const (
+ defaultClientTimeout = 10 * time.Second
+ telegramAPIEndpoint = "https://api.telegram.org"
+
+ MarkdownFormatting = "Markdown"
+ MarkdownV2Formatting = "MarkdownV2"
+ HTMLFormatting = "HTML"
+)
+
+type Client struct {
+ botToken string
+ chatID string
+}
+
+func NewClient(botToken, chatID string) *Client {
+ return &Client{
+ botToken: botToken,
+ chatID: chatID,
+ }
+}
+
+// Specs: https://core.telegram.org/bots/api#getme
+func (c *Client) GetMe() (*User, error) {
+ endpointURL, err := url.JoinPath(telegramAPIEndpoint, "/bot"+c.botToken, "/getMe")
+ if err != nil {
+ return nil, fmt.Errorf("telegram: unable to join base URL and path: %w", err)
+ }
+
+ request, err := http.NewRequest(http.MethodGet, endpointURL, nil)
+ if err != nil {
+ return nil, fmt.Errorf("telegram: unable to create request: %v", err)
+ }
+
+ request.Header.Set("Accept", "application/json")
+ request.Header.Set("User-Agent", "Miniflux/"+version.Version)
+
+ httpClient := &http.Client{Timeout: defaultClientTimeout}
+ response, err := httpClient.Do(request)
+ if err != nil {
+ return nil, fmt.Errorf("telegram: unable to send request: %v", err)
+ }
+ defer response.Body.Close()
+
+ var userResponse UserResponse
+ if err := json.NewDecoder(response.Body).Decode(&userResponse); err != nil {
+ return nil, fmt.Errorf("telegram: unable to decode user response: %w", err)
+ }
+
+ if !userResponse.Ok {
+ return nil, fmt.Errorf("telegram: unable to send message: %s (error code is %d)", userResponse.Description, userResponse.ErrorCode)
+ }
+
+ return &userResponse.Result, nil
+}
+
+// Specs: https://core.telegram.org/bots/api#sendmessage
+func (c *Client) SendMessage(message *MessageRequest) (*Message, error) {
+ endpointURL, err := url.JoinPath(telegramAPIEndpoint, "/bot"+c.botToken, "/sendMessage")
+ if err != nil {
+ return nil, fmt.Errorf("telegram: unable to join base URL and path: %w", err)
+ }
+
+ requestBody, err := json.Marshal(message)
+ if err != nil {
+ return nil, fmt.Errorf("telegram: unable to encode request body: %v", err)
+ }
+
+ request, err := http.NewRequest(http.MethodPost, endpointURL, bytes.NewReader(requestBody))
+ if err != nil {
+ return nil, fmt.Errorf("telegram: unable to create request: %v", err)
+ }
+
+ request.Header.Set("Content-Type", "application/json")
+ request.Header.Set("Accept", "application/json")
+ request.Header.Set("User-Agent", "Miniflux/"+version.Version)
+
+ httpClient := &http.Client{Timeout: defaultClientTimeout}
+ response, err := httpClient.Do(request)
+ if err != nil {
+ return nil, fmt.Errorf("telegram: unable to send request: %v", err)
+ }
+ defer response.Body.Close()
+
+ var messageResponse MessageResponse
+ if err := json.NewDecoder(response.Body).Decode(&messageResponse); err != nil {
+ return nil, fmt.Errorf("telegram: unable to decode discovery response: %w", err)
+ }
+
+ if !messageResponse.Ok {
+ return nil, fmt.Errorf("telegram: unable to send message: %s (error code is %d)", messageResponse.Description, messageResponse.ErrorCode)
+ }
+
+ return &messageResponse.Result, nil
+}
+
+type InlineKeyboard struct {
+ InlineKeyboard []InlineKeyboardRow `json:"inline_keyboard"`
+}
+
+type InlineKeyboardRow []*InlineKeyboardButton
+
+type InlineKeyboardButton struct {
+ Text string `json:"text"`
+ URL string `json:"url,omitempty"`
+}
+
+type User struct {
+ ID int64 `json:"id"`
+ IsBot bool `json:"is_bot"`
+ FirstName string `json:"first_name"`
+ LastName string `json:"last_name"`
+ Username string `json:"username"`
+ LanguageCode string `json:"language_code"`
+ IsPremium bool `json:"is_premium"`
+ CanJoinGroups bool `json:"can_join_groups"`
+ CanReadAllGroupMessages bool `json:"can_read_all_group_messages"`
+ SupportsInlineQueries bool `json:"supports_inline_queries"`
+}
+
+type Chat struct {
+ ID int64 `json:"id"`
+ Type string `json:"type"`
+ Title string `json:"title"`
+}
+
+type Message struct {
+ MessageID int64 `json:"message_id"`
+ From User `json:"from"`
+ Chat Chat `json:"chat"`
+ MessageThreadID int64 `json:"message_thread_id"`
+ Date int64 `json:"date"`
+}
+
+type BaseResponse struct {
+ Ok bool `json:"ok"`
+ ErrorCode int `json:"error_code"`
+ Description string `json:"description"`
+}
+
+type UserResponse struct {
+ BaseResponse
+ Result User `json:"result"`
+}
+
+type MessageRequest struct {
+ ChatID string `json:"chat_id"`
+ MessageThreadID int64 `json:"message_thread_id,omitempty"`
+ Text string `json:"text"`
+ ParseMode string `json:"parse_mode,omitempty"`
+ DisableWebPagePreview bool `json:"disable_web_page_preview"`
+ DisableNotification bool `json:"disable_notification"`
+ ReplyMarkup *InlineKeyboard `json:"reply_markup,omitempty"`
+}
+
+type MessageResponse struct {
+ BaseResponse
+ Result Message `json:"result"`
+}