package connectors import ( "bytes" "context" "encoding/json" "errors" "fmt" "io" "mime/multipart" "net/http" "strconv" "time" ) type TelegramConfig struct { BotToken string Timeout time.Duration } type TelegramConnector struct { cfg TelegramConfig client *http.Client } func NewTelegramConnector(cfg TelegramConfig) *TelegramConnector { timeout := cfg.Timeout if timeout == 0 { timeout = 30 * time.Second } return &TelegramConnector{ cfg: cfg, client: &http.Client{ Timeout: timeout, }, } } func (t *TelegramConnector) ID() string { return "telegram" } func (t *TelegramConnector) Name() string { return "Telegram" } func (t *TelegramConnector) Description() string { return "Send messages via Telegram Bot API" } func (t *TelegramConnector) GetActions() []Action { return []Action{ { Name: "send_message", Description: "Send a text message", Schema: map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "chat_id": map[string]interface{}{"type": "string", "description": "Chat ID or @username"}, "text": map[string]interface{}{"type": "string", "description": "Message text"}, "parse_mode": map[string]interface{}{"type": "string", "enum": []string{"HTML", "Markdown", "MarkdownV2"}}, }, }, Required: []string{"chat_id", "text"}, }, { Name: "send_document", Description: "Send a document/file", Schema: map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "chat_id": map[string]interface{}{"type": "string", "description": "Chat ID"}, "document": map[string]interface{}{"type": "string", "description": "File path or URL"}, "caption": map[string]interface{}{"type": "string", "description": "Document caption"}, }, }, Required: []string{"chat_id", "document"}, }, { Name: "send_photo", Description: "Send a photo", Schema: map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "chat_id": map[string]interface{}{"type": "string", "description": "Chat ID"}, "photo": map[string]interface{}{"type": "string", "description": "Photo URL or file_id"}, "caption": map[string]interface{}{"type": "string", "description": "Photo caption"}, }, }, Required: []string{"chat_id", "photo"}, }, } } func (t *TelegramConnector) Validate(params map[string]interface{}) error { if _, ok := params["chat_id"]; !ok { return errors.New("'chat_id' is required") } return nil } func (t *TelegramConnector) Execute(ctx context.Context, action string, params map[string]interface{}) (interface{}, error) { switch action { case "send_message": return t.sendMessage(ctx, params) case "send_document": return t.sendDocument(ctx, params) case "send_photo": return t.sendPhoto(ctx, params) default: return nil, errors.New("unknown action: " + action) } } func (t *TelegramConnector) sendMessage(ctx context.Context, params map[string]interface{}) (interface{}, error) { chatID := params["chat_id"].(string) text := params["text"].(string) payload := map[string]interface{}{ "chat_id": chatID, "text": text, } if parseMode, ok := params["parse_mode"].(string); ok { payload["parse_mode"] = parseMode } return t.apiCall(ctx, "sendMessage", payload) } func (t *TelegramConnector) sendDocument(ctx context.Context, params map[string]interface{}) (interface{}, error) { chatID := params["chat_id"].(string) document := params["document"].(string) payload := map[string]interface{}{ "chat_id": chatID, "document": document, } if caption, ok := params["caption"].(string); ok { payload["caption"] = caption } return t.apiCall(ctx, "sendDocument", payload) } func (t *TelegramConnector) sendPhoto(ctx context.Context, params map[string]interface{}) (interface{}, error) { chatID := params["chat_id"].(string) photo := params["photo"].(string) payload := map[string]interface{}{ "chat_id": chatID, "photo": photo, } if caption, ok := params["caption"].(string); ok { payload["caption"] = caption } return t.apiCall(ctx, "sendPhoto", payload) } func (t *TelegramConnector) apiCall(ctx context.Context, method string, payload map[string]interface{}) (interface{}, error) { url := fmt.Sprintf("https://api.telegram.org/bot%s/%s", t.cfg.BotToken, method) body, err := json.Marshal(payload) if err != nil { return nil, err } req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewReader(body)) if err != nil { return nil, err } req.Header.Set("Content-Type", "application/json") resp, err := t.client.Do(req) if err != nil { return nil, err } defer resp.Body.Close() respBody, err := io.ReadAll(resp.Body) if err != nil { return nil, err } var result map[string]interface{} if err := json.Unmarshal(respBody, &result); err != nil { return nil, err } if ok, exists := result["ok"].(bool); exists && !ok { desc := "unknown error" if d, exists := result["description"].(string); exists { desc = d } return result, errors.New("Telegram API error: " + desc) } return result, nil } func (t *TelegramConnector) SendFileFromBytes(ctx context.Context, chatID string, filename string, content []byte, caption string) (interface{}, error) { url := fmt.Sprintf("https://api.telegram.org/bot%s/sendDocument", t.cfg.BotToken) var b bytes.Buffer w := multipart.NewWriter(&b) w.WriteField("chat_id", chatID) if caption != "" { w.WriteField("caption", caption) } fw, err := w.CreateFormFile("document", filename) if err != nil { return nil, err } fw.Write(content) w.Close() req, err := http.NewRequestWithContext(ctx, "POST", url, &b) if err != nil { return nil, err } req.Header.Set("Content-Type", w.FormDataContentType()) resp, err := t.client.Do(req) if err != nil { return nil, err } defer resp.Body.Close() respBody, err := io.ReadAll(resp.Body) if err != nil { return nil, err } var result map[string]interface{} if err := json.Unmarshal(respBody, &result); err != nil { return nil, err } return result, nil } func (t *TelegramConnector) GetChatID(chatIDOrUsername interface{}) string { switch v := chatIDOrUsername.(type) { case string: return v case int: return strconv.Itoa(v) case int64: return strconv.FormatInt(v, 10) case float64: return strconv.FormatInt(int64(v), 10) default: return fmt.Sprintf("%v", v) } }