- Add Telegram integration support by introducing a dispatcher for handling Telegram events and messages. - Implement event notifications for robot configuration changes (creation, update, deletion) to facilitate integration with external services. - Refactor the robot initialization process to load robots into cache and start the dispatcher, improving the overall system setup. - Update the delivery event structure to include additional metadata for better context during message handling. - Enhance logging capabilities for better observability during robot execution and event processing.
200 lines
5.8 KiB
Go
200 lines
5.8 KiB
Go
package telegram
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"strings"
|
|
|
|
"github.com/go-telegram/bot"
|
|
"github.com/go-telegram/bot/models"
|
|
"github.com/yaoapp/yao/attachment"
|
|
)
|
|
|
|
// SendMessage sends a message to a chat. If the text contains Markdown formatting,
|
|
// it is automatically converted to Telegram-compatible HTML.
|
|
func (b *Bot) SendMessage(ctx context.Context, chatID int64, text string, replyTo int64) error {
|
|
sdk, err := b.sdk()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
formatted := FormatTelegramHTML(text)
|
|
params := &bot.SendMessageParams{
|
|
ChatID: chatID,
|
|
Text: formatted,
|
|
ParseMode: models.ParseModeHTML,
|
|
}
|
|
if replyTo > 0 {
|
|
params.ReplyParameters = &models.ReplyParameters{MessageID: int(replyTo)}
|
|
}
|
|
_, err = sdk.SendMessage(ctx, params)
|
|
return err
|
|
}
|
|
|
|
// MediaType indicates which Telegram send method to use.
|
|
type MediaType string
|
|
|
|
const (
|
|
MediaPhoto MediaType = "photo"
|
|
MediaDocument MediaType = "document"
|
|
MediaAudio MediaType = "audio"
|
|
MediaVideo MediaType = "video"
|
|
MediaVoice MediaType = "voice"
|
|
MediaAnimation MediaType = "animation"
|
|
MediaSticker MediaType = "sticker"
|
|
)
|
|
|
|
// SendMedia sends a media message from a Yao attachment wrapper
|
|
// (e.g. "__yao.attachment://ccd472d11feb96e03a3fc468f494045c").
|
|
// It reads the file from the attachment manager, detects the media type from
|
|
// the stored content type, and uploads it to the Telegram chat.
|
|
func (b *Bot) SendMedia(ctx context.Context, chatID int64, wrapper string, caption string, replyTo int64) error {
|
|
managerName, fileID, err := parseWrapper(wrapper)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
manager, exists := attachment.Managers[managerName]
|
|
if !exists {
|
|
return fmt.Errorf("attachment manager %s not found", managerName)
|
|
}
|
|
|
|
resp, err := manager.Download(ctx, fileID)
|
|
if err != nil {
|
|
return fmt.Errorf("attachment download %s: %w", fileID, err)
|
|
}
|
|
defer resp.Reader.Close()
|
|
|
|
mediaType := DetectMediaType(resp.ContentType)
|
|
filename := fileID + resp.Extension
|
|
|
|
file := &models.InputFileUpload{Filename: filename, Data: resp.Reader}
|
|
return b.sendMedia(ctx, chatID, mediaType, file, caption, replyTo)
|
|
}
|
|
|
|
// SendMediaByURL sends a media message from a public URL.
|
|
// Telegram downloads the file directly from the URL.
|
|
func (b *Bot) SendMediaByURL(ctx context.Context, chatID int64, mediaType MediaType, url string, caption string, replyTo int64) error {
|
|
file := &models.InputFileString{Data: url}
|
|
return b.sendMedia(ctx, chatID, mediaType, file, caption, replyTo)
|
|
}
|
|
|
|
// SendMediaByReader sends a media message by uploading raw bytes.
|
|
func (b *Bot) SendMediaByReader(ctx context.Context, chatID int64, mediaType MediaType, filename string, data io.Reader, caption string, replyTo int64) error {
|
|
file := &models.InputFileUpload{Filename: filename, Data: data}
|
|
return b.sendMedia(ctx, chatID, mediaType, file, caption, replyTo)
|
|
}
|
|
|
|
// DetectMediaType guesses the MediaType from a MIME string.
|
|
// Falls back to MediaDocument for unknown types.
|
|
func DetectMediaType(mimeType string) MediaType {
|
|
lower := strings.ToLower(mimeType)
|
|
switch {
|
|
case strings.HasPrefix(lower, "image/webp"):
|
|
return MediaSticker
|
|
case strings.HasPrefix(lower, "image/gif"):
|
|
return MediaAnimation
|
|
case strings.HasPrefix(lower, "image/"):
|
|
return MediaPhoto
|
|
case strings.HasPrefix(lower, "video/"):
|
|
return MediaVideo
|
|
case strings.HasPrefix(lower, "audio/ogg"):
|
|
return MediaVoice
|
|
case strings.HasPrefix(lower, "audio/"):
|
|
return MediaAudio
|
|
default:
|
|
return MediaDocument
|
|
}
|
|
}
|
|
|
|
// parseWrapper splits "__yao.attachment://fileID" into manager name and file ID.
|
|
func parseWrapper(wrapper string) (managerName string, fileID string, err error) {
|
|
idx := strings.Index(wrapper, "://")
|
|
if idx < 0 {
|
|
return "", "", fmt.Errorf("invalid attachment wrapper: %s", wrapper)
|
|
}
|
|
return wrapper[:idx], wrapper[idx+3:], nil
|
|
}
|
|
|
|
func (b *Bot) sendMedia(ctx context.Context, chatID int64, mediaType MediaType, file models.InputFile, caption string, replyTo int64) error {
|
|
sdk, err := b.sdk()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var replyParams *models.ReplyParameters
|
|
if replyTo > 0 {
|
|
replyParams = &models.ReplyParameters{MessageID: int(replyTo)}
|
|
}
|
|
|
|
htmlCaption := FormatTelegramHTML(caption)
|
|
|
|
switch mediaType {
|
|
case MediaPhoto:
|
|
_, err = sdk.SendPhoto(ctx, &bot.SendPhotoParams{
|
|
ChatID: chatID,
|
|
Photo: file,
|
|
Caption: htmlCaption,
|
|
ParseMode: models.ParseModeHTML,
|
|
ReplyParameters: replyParams,
|
|
})
|
|
|
|
case MediaDocument:
|
|
_, err = sdk.SendDocument(ctx, &bot.SendDocumentParams{
|
|
ChatID: chatID,
|
|
Document: file,
|
|
Caption: htmlCaption,
|
|
ParseMode: models.ParseModeHTML,
|
|
ReplyParameters: replyParams,
|
|
})
|
|
|
|
case MediaAudio:
|
|
_, err = sdk.SendAudio(ctx, &bot.SendAudioParams{
|
|
ChatID: chatID,
|
|
Audio: file,
|
|
Caption: htmlCaption,
|
|
ParseMode: models.ParseModeHTML,
|
|
ReplyParameters: replyParams,
|
|
})
|
|
|
|
case MediaVideo:
|
|
_, err = sdk.SendVideo(ctx, &bot.SendVideoParams{
|
|
ChatID: chatID,
|
|
Video: file,
|
|
Caption: htmlCaption,
|
|
ParseMode: models.ParseModeHTML,
|
|
ReplyParameters: replyParams,
|
|
})
|
|
|
|
case MediaVoice:
|
|
_, err = sdk.SendVoice(ctx, &bot.SendVoiceParams{
|
|
ChatID: chatID,
|
|
Voice: file,
|
|
Caption: htmlCaption,
|
|
ParseMode: models.ParseModeHTML,
|
|
ReplyParameters: replyParams,
|
|
})
|
|
|
|
case MediaAnimation:
|
|
_, err = sdk.SendAnimation(ctx, &bot.SendAnimationParams{
|
|
ChatID: chatID,
|
|
Animation: file,
|
|
Caption: htmlCaption,
|
|
ParseMode: models.ParseModeHTML,
|
|
ReplyParameters: replyParams,
|
|
})
|
|
|
|
case MediaSticker:
|
|
_, err = sdk.SendSticker(ctx, &bot.SendStickerParams{
|
|
ChatID: chatID,
|
|
Sticker: file,
|
|
ReplyParameters: replyParams,
|
|
})
|
|
|
|
default:
|
|
return fmt.Errorf("unsupported media type: %s", mediaType)
|
|
}
|
|
|
|
return err
|
|
}
|