Thumbnail

rani/matterbridge.git

Clone URL: https://git.buni.party/rani/matterbridge.git

Viewing file on branch master

1package btelegram
2
3import (
4 "fmt"
5 "html"
6 "log"
7 "strconv"
8 "strings"
9
10 "github.com/matterbridge-org/matterbridge/bridge"
11 "github.com/matterbridge-org/matterbridge/bridge/config"
12 "github.com/matterbridge-org/matterbridge/bridge/helper"
13 // Seems not much different from upstream https://github.com/go-telegram-bot-api/telegram-bot-api replace?
14 tgbotapi "github.com/matterbridge/telegram-bot-api/v6"
15)
16
17const (
18 unknownUser = "unknown"
19 HTMLFormat = "HTML"
20 HTMLNick = "htmlnick"
21 MarkdownV2 = "MarkdownV2"
22)
23
24type Btelegram struct {
25 c *tgbotapi.BotAPI
26 *bridge.Config
27 avatarMap map[string]string // keep cache of userid and avatar sha
28}
29
30func New(cfg *bridge.Config) bridge.Bridger {
31 tgsConvertFormat := cfg.GetString("MediaConvertTgs")
32 if tgsConvertFormat != "" {
33 err := helper.CanConvertTgsToX()
34 if err != nil {
35 log.Fatalf("Telegram bridge configured to convert .tgs files to '%s', but %s does not appear to work:\n%#v", tgsConvertFormat, helper.LottieBackend(), err)
36 }
37 if !helper.SupportsFormat(tgsConvertFormat) {
38 log.Fatalf("Telegram bridge configured to convert .tgs files to '%s', but %s doesn't support it.", tgsConvertFormat, helper.LottieBackend())
39 }
40 }
41 return &Btelegram{Config: cfg, avatarMap: make(map[string]string)}
42}
43
44func (b *Btelegram) Connect() error {
45 var err error
46 b.Log.Info("Connecting")
47 b.c, err = tgbotapi.NewBotAPI(b.GetString("Token"))
48 if err != nil {
49 b.Log.Debugf("%#v", err)
50 return err
51 }
52 u := tgbotapi.NewUpdate(0)
53 u.Timeout = 60
54 updates := b.c.GetUpdatesChan(u)
55 b.Log.Info("Connection succeeded")
56 go b.handleRecv(updates)
57 return nil
58}
59
60func (b *Btelegram) Disconnect() error {
61 return nil
62}
63
64func (b *Btelegram) JoinChannel(channel config.ChannelInfo) error {
65 return nil
66}
67
68func TGGetParseMode(b *Btelegram, username string, text string) (textout string, parsemode string) {
69 textout = username + text
70 if b.GetString("MessageFormat") == HTMLFormat {
71 b.Log.Debug("Using mode HTML")
72 parsemode = tgbotapi.ModeHTML
73 }
74 if b.GetString("MessageFormat") == "Markdown" {
75 b.Log.Debug("Using mode markdown")
76 parsemode = tgbotapi.ModeMarkdown
77 }
78 if b.GetString("MessageFormat") == MarkdownV2 {
79 b.Log.Debug("Using mode MarkdownV2")
80 parsemode = MarkdownV2
81 }
82 if strings.ToLower(b.GetString("MessageFormat")) == HTMLNick {
83 b.Log.Debug("Using mode HTML - nick only")
84 textout = username + html.EscapeString(text)
85 parsemode = tgbotapi.ModeHTML
86 }
87 return textout, parsemode
88}
89
90func (b *Btelegram) getIds(channel string) (int64, int, error) {
91 var chatid int64
92 topicid := 0
93
94 // get the chatid
95 if strings.Contains(channel, "/") { //nolint:nestif
96 s := strings.Split(channel, "/")
97 if len(s) < 2 {
98 b.Log.Errorf("Invalid channel format: %#v\n", channel)
99 return 0, 0, nil
100 }
101 id, err := strconv.ParseInt(s[0], 10, 64)
102 if err != nil {
103 return 0, 0, err
104 }
105 chatid = id
106 tid, err := strconv.Atoi(s[1])
107 if err != nil {
108 return 0, 0, err
109 }
110 topicid = tid
111 } else {
112 id, err := strconv.ParseInt(channel, 10, 64)
113 if err != nil {
114 return 0, 0, err
115 }
116 chatid = id
117 }
118 return chatid, topicid, nil
119}
120
121func (b *Btelegram) Send(msg config.Message) (string, error) {
122 b.Log.Debugf("=> Receiving %#v", msg)
123
124 chatid, topicid, err := b.getIds(msg.Channel)
125 if err != nil {
126 return "", err
127 }
128
129 // map the file SHA to our user (caches the avatar)
130 if msg.Event == config.EventAvatarDownload {
131 return b.cacheAvatar(&msg)
132 }
133
134 if b.GetString("MessageFormat") == HTMLFormat {
135 msg.Text = makeHTML(html.EscapeString(msg.Text))
136 }
137
138 // Delete message
139 if msg.Event == config.EventMsgDelete {
140 return b.handleDelete(&msg, chatid)
141 }
142
143 // Handle prefix hint for unthreaded messages.
144 if msg.ParentNotFound() {
145 msg.ParentID = ""
146 msg.Text = fmt.Sprintf("[reply]: %s", msg.Text)
147 }
148
149 var parentID int
150 if msg.ParentID != "" {
151 parentID, _ = b.intParentID(msg.ParentID)
152 }
153
154 // Upload a file if it exists
155 if msg.Extra != nil {
156 for _, rmsg := range helper.HandleExtra(&msg, b.General) {
157 if _, msgErr := b.sendMessage(chatid, topicid, rmsg.Username, rmsg.Text, parentID); msgErr != nil {
158 b.Log.Errorf("sendMessage failed: %s", msgErr)
159 }
160 }
161 // check if we have files to upload (from slack, telegram or mattermost)
162 if len(msg.Extra["file"]) > 0 {
163 return b.handleUploadFile(&msg, chatid, topicid, parentID)
164 }
165 }
166
167 // edit the message if we have a msg ID
168 if msg.ID != "" {
169 return b.handleEdit(&msg, chatid)
170 }
171
172 // Post normal message
173 // TODO: recheck it.
174 // Ignore empty text field needs for prevent double messages from whatsapp to telegram
175 // when sending media with text caption
176 if msg.Text != "" {
177 return b.sendMessage(chatid, topicid, msg.Username, msg.Text, parentID)
178 }
179
180 return "", nil
181}
182
183func (b *Btelegram) getFileDirectURL(id string) string {
184 res, err := b.c.GetFileDirectURL(id)
185 if err != nil {
186 return ""
187 }
188 return res
189}
190
191func (b *Btelegram) sendMessage(chatid int64, topicid int, username, text string, parentID int) (string, error) {
192 m := tgbotapi.NewMessage(chatid, "")
193 m.Text, m.ParseMode = TGGetParseMode(b, username, text)
194 if topicid != 0 {
195 m.BaseChat.MessageThreadID = topicid
196 }
197 m.ReplyToMessageID = parentID
198 m.DisableWebPagePreview = b.GetBool("DisableWebPagePreview")
199
200 res, err := b.c.Send(m)
201 if err != nil {
202 return "", err
203 }
204 return strconv.Itoa(res.MessageID), nil
205}
206
207// sendMediaFiles native upload media files via media group
208func (b *Btelegram) sendMediaFiles(msg *config.Message, chatid int64, threadid int, parentID int, media []interface{}) (string, error) {
209 if len(media) == 0 {
210 return "", nil
211 }
212 mg := tgbotapi.MediaGroupConfig{
213 BaseChat: tgbotapi.BaseChat{
214 ChatID: chatid,
215 MessageThreadID: threadid,
216 ChannelUsername: msg.Username,
217 ReplyToMessageID: parentID,
218 },
219 Media: media,
220 }
221 messages, err := b.c.SendMediaGroup(mg)
222 if err != nil {
223 return "", err
224 }
225 // return first message id
226 return strconv.Itoa(messages[0].MessageID), nil
227}
228
229// intParentID return integer parent id for telegram message
230func (b *Btelegram) intParentID(parentID string) (int, error) {
231 pid, err := strconv.Atoi(parentID)
232 if err != nil {
233 return 0, err
234 }
235 return pid, nil
236}
237
238func (b *Btelegram) cacheAvatar(msg *config.Message) (string, error) {
239 fi := msg.Extra["file"][0].(config.FileInfo)
240 /* if we have a sha we have successfully uploaded the file to the media server,
241 so we can now cache the sha */
242 if fi.SHA != "" {
243 b.Log.Debugf("Added %s to %s in avatarMap", fi.SHA, msg.UserID)
244 b.avatarMap[msg.UserID] = fi.SHA
245 }
246 return "", nil
247}
248