Major changes: - Add Go backend (backend/) with microservices architecture - Enhanced master-agents-svc: reranker, content-classifier, stealth-crawler, proxy-manager, media-search, fastClassifier, language detection - New web-svc widgets: KnowledgeCard, ProductCard, ProfileCard, VideoCard, UnifiedCard, CardGallery, InlineImageGallery, SourcesPanel, RelatedQuestions - Improved discover-svc with discover-db integration - Docker deployment improvements (Caddyfile, vendor.sh, BUILD.md) - Library-svc: project_id schema migration - Remove deprecated finance-svc and travel-svc - Localization improvements across services Made-with: Cursor
264 lines
6.3 KiB
Go
264 lines
6.3 KiB
Go
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)
|
|
}
|
|
}
|