feat(channels/telegram): Telegram 渠道插件(Bot API 长轮询/Webhook + file_id 媒体 + 主动推送强)
This commit is contained in:
1 parent
0b036645bb
commit
f9f5a78a02
12 files changed
+1983
No files matched your search
@@ -0,0 +1,94 @@
|
||||
package telegram
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/channel/adapter"
|
||||
)
|
||||
|
||||
// markdownParseMode is the parse_mode sent with outgoing text/captions so the
|
||||
// Markdown capability is actually exercised.
|
||||
const markdownParseMode = "Markdown"
|
||||
|
||||
// SendMessage delivers the reply messages contained in resp to their target
|
||||
// chats. Each message goes to its own ChannelID (or GroupID) conversation.
|
||||
func (a *Adapter) SendMessage(ctx context.Context, resp adapter.UnifiedResponse) error {
|
||||
if err := a.requireConnected(); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, m := range resp.Messages {
|
||||
if err := a.sendOne(ctx, m, resp.ReplyTo); err != nil {
|
||||
return fmt.Errorf("telegram: send message: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SendProactive pushes a message not triggered by a recent inbound message.
|
||||
// Telegram permits arbitrary proactive push, so this is only gated by the
|
||||
// connection state.
|
||||
func (a *Adapter) SendProactive(ctx context.Context, msg adapter.UnifiedMessage) error {
|
||||
if err := a.requireConnected(); err != nil {
|
||||
return err
|
||||
}
|
||||
return a.sendOne(ctx, msg, "")
|
||||
}
|
||||
|
||||
// sendOne delivers a single message to its conversation, choosing the Bot API
|
||||
// method that matches the content type and the presence of a file_id payload.
|
||||
func (a *Adapter) sendOne(ctx context.Context, m adapter.UnifiedMessage, replyTo string) error {
|
||||
chatID := m.ChannelID
|
||||
if chatID == "" {
|
||||
chatID = m.GroupID
|
||||
}
|
||||
if chatID == "" {
|
||||
return fmt.Errorf("telegram: %w", ErrNoChatTarget)
|
||||
}
|
||||
replyToID := parseReplyToID(replyTo)
|
||||
fileID := mediaFileID(m.Media)
|
||||
|
||||
switch m.ContentType {
|
||||
case adapter.ContentImage:
|
||||
if fileID != "" {
|
||||
_, err := a.sendPhoto(ctx, chatID, fileID, replyToID, m.Content)
|
||||
return err
|
||||
}
|
||||
case adapter.ContentVoice:
|
||||
if fileID != "" {
|
||||
_, err := a.sendVoice(ctx, chatID, fileID, replyToID, m.Content)
|
||||
return err
|
||||
}
|
||||
case adapter.ContentFile:
|
||||
if fileID != "" {
|
||||
_, err := a.sendDocument(ctx, chatID, fileID, mediaFileName(m.Media), replyToID, m.Content)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if m.Content == "" {
|
||||
return fmt.Errorf("telegram: %w: empty message content", ErrBadResponse)
|
||||
}
|
||||
_, err := a.sendMessage(ctx, chatID, m.Content, replyToID, markdownParseMode)
|
||||
return err
|
||||
}
|
||||
|
||||
// mediaFileID returns the first non-empty file_id carried by the media list.
|
||||
func mediaFileID(media []adapter.Media) string {
|
||||
for _, md := range media {
|
||||
if md.URL != "" {
|
||||
return md.URL
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// mediaFileName returns the first non-empty file name carried by the media list.
|
||||
func mediaFileName(media []adapter.Media) string {
|
||||
for _, md := range media {
|
||||
if md.Name != "" {
|
||||
return md.Name
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
Reference in new issue
Block a user