blob: 7d234ffd0e0675f46982e43c1f9f0c084264de99 [file] [log] [blame]
Sergiusz Bazanskia8854882020-01-05 00:34:38 +01001package main
2
3import (
4 "context"
5 "fmt"
6 "strings"
7 "time"
8
9 tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api"
10 "github.com/golang/glog"
11)
12
13// telegramConnection runs a long-lived connection to the Telegram API to receive
14// updates and pipe resulting messages into telLog.
15func (s *server) telegramConnection(ctx context.Context) error {
16 u := tgbotapi.NewUpdate(0)
17 // TODO(q3k): figure out what the _fuck_ does this even mean
18 u.Timeout = 60
19
20 updates, err := s.tel.GetUpdatesChan(u)
21 if err != nil {
22 return fmt.Errorf("GetUpdatesChan(%+v): %v", u, err)
23 }
Michal Zagorski5b1aa132020-03-01 17:05:05 +010024 glog.V(8).Infof("telegram/debug8: New update")
Sergiusz Bazanskia8854882020-01-05 00:34:38 +010025
26 for {
27 select {
28 case <-ctx.Done():
29 return ctx.Err()
Michal Zagorski5b1aa132020-03-01 17:05:05 +010030 case update, ok := <-updates:
Sergiusz Bazanskia8854882020-01-05 00:34:38 +010031 if !ok {
32 return fmt.Errorf("Updates channel closed")
33 }
34
35 // Dispatch update.
36 switch {
Michal Zagorski5b1aa132020-03-01 17:05:05 +010037 case update.Message != nil:
38 glog.V(4).Infof("telegram/debug4: New message: %d", update.Message.Chat.ID)
39 if update.Message.Chat.ID != s.groupId {
40 glog.Infof("[ignored group %d] <%s> %v", update.Message.Chat.ID, update.Message.From, update.Message.Text)
Sergiusz Bazanskia8854882020-01-05 00:34:38 +010041 continue
42 }
Michal Zagorski5b1aa132020-03-01 17:05:05 +010043 date := time.Unix(int64(update.Message.Date), 0)
Sergiusz Bazanski83e26902020-01-23 14:18:25 +010044 if time.Since(date) > 2*time.Minute {
Michal Zagorski5b1aa132020-03-01 17:05:05 +010045 glog.Infof("[old message] <%s> %v", update.Message.From, update.Message.Text)
Sergiusz Bazanski83e26902020-01-23 14:18:25 +010046 continue
47 }
Michal Zagorski5b1aa132020-03-01 17:05:05 +010048 if msg := plainFromTelegram(s.tel.Self.ID, &update); msg != nil {
Sergiusz Bazanskia8854882020-01-05 00:34:38 +010049 s.telLog <- msg
50 }
51 }
52 }
53 }
54}
55
56// telegramLoop maintains a telegramConnection.
57func (s *server) telegramLoop(ctx context.Context) {
58 for {
Michal Zagorski5b1aa132020-03-01 17:05:05 +010059 glog.V(4).Info("telegram/debug4: Starting telegram connection loop")
Sergiusz Bazanskia8854882020-01-05 00:34:38 +010060 err := s.telegramConnection(ctx)
61 if err == ctx.Err() {
62 glog.Infof("Telegram connection closing: %v", err)
63 return
64 }
65
66 glog.Errorf("Telegram connection error: %v", err)
67 select {
68 case <-ctx.Done():
69 return
70 case <-time.After(1 * time.Second):
71 continue
72 }
73 }
74}
75
76// plainFromTelegram turns a Telegram message into a plain text message.
77func plainFromTelegram(selfID int, u *tgbotapi.Update) *telegramPlain {
78 parts := []string{}
79
80 from := u.Message.From
81 replyto := u.Message.ReplyToMessage
82 text := u.Message.Text
83
84 // This message is in reply to someone.
85 if replyto != nil && text != "" && replyto.From != nil {
86 // The rendered name of the author of the quote.
87 ruid := "@" + replyto.From.String()
88
89 // First line of the quoted text.
90 quotedLine := ""
91
92 // Check if the quoted message is from our bridge.
93 if replyto.From.ID == selfID {
94 // Someone replied to an IRC bridge message, extract nick and line from there
95 // eg: "<q3k> foo bar baz" -> ruid = q3k; quotedLine = foo bar baz
96 t := replyto.Text
97 if strings.HasPrefix(t, "<") {
98 p := strings.SplitN(t[1:], ">", 2)
99 nick := p[0]
100 quoted := strings.TrimSpace(p[1])
101
102 // ensure nick looks sane
103 if len(nick) < 16 && len(strings.Fields(nick)) == 1 {
104 quotedLine = strings.TrimSpace(strings.Split(quoted, "\n")[0])
105 ruid = nick
106 }
107 }
108 } else {
109 // Someone replied to a native telegram message.
110 quoted := strings.TrimSpace(replyto.Text)
111 quotedLine = strings.TrimSpace(strings.Split(quoted, "\n")[0])
112 }
113
114 // If we have a line, quote it. Otherwise just refer to the nick without a quote.
115 if quotedLine != "" {
116 parts = append(parts, fmt.Sprintf("%s: >%s\n", ruid, quotedLine))
117 } else {
118 parts = append(parts, fmt.Sprintf("%s: ", ruid))
119 }
120 }
121
122 // This message contains a sticker.
123 if u.Message.Sticker != nil {
124 emoji := ""
125 if u.Message.Sticker.SetName != "" {
126 emoji += "/" + u.Message.Sticker.SetName
127 }
128 if u.Message.Sticker.Emoji != "" {
129 emoji += "/" + u.Message.Sticker.Emoji
130 }
131 parts = append(parts, fmt.Sprintf("<sticker%s>", emoji))
132 }
133
Sergiusz Bazanskia07688f2020-01-05 21:21:07 +0100134 // Mutually exclusive stuff.
135 switch {
136 case u.Message.Animation != nil:
137 // This message contains an animation.
Sergiusz Bazanskia8854882020-01-05 00:34:38 +0100138 a := u.Message.Animation
139 parts = append(parts, fmt.Sprintf("<uploaded animation: %s >\n", fileURL(a.FileID, "mp4")))
Sergiusz Bazanskia8854882020-01-05 00:34:38 +0100140
Sergiusz Bazanskia07688f2020-01-05 21:21:07 +0100141 case u.Message.Document != nil:
142 // This message contains a document.
Sergiusz Bazanskia8854882020-01-05 00:34:38 +0100143 d := u.Message.Document
144 fnp := strings.Split(d.FileName, ".")
145 ext := "bin"
146 if len(fnp) > 1 {
147 ext = fnp[len(fnp)-1]
148 }
149 parts = append(parts, fmt.Sprintf("<uploaded file: %s >\n", fileURL(d.FileID, ext)))
Sergiusz Bazanskia8854882020-01-05 00:34:38 +0100150
Sergiusz Bazanskia07688f2020-01-05 21:21:07 +0100151 case u.Message.Photo != nil:
152 // This message contains a photo.
Sergiusz Bazanskia8854882020-01-05 00:34:38 +0100153 // Multiple entries are for different file sizes, choose the highest quality one.
154 hq := (*u.Message.Photo)[0]
155 for _, p := range *u.Message.Photo {
156 if p.FileSize > hq.FileSize {
157 hq = p
158 }
159 }
160 parts = append(parts, fmt.Sprintf("<uploaded photo: %s >\n", fileURL(hq.FileID, "jpg")))
161 }
162
163 // This message has some plain text.
164 if text != "" {
165 parts = append(parts, text)
166 }
167
168 // Was there anything that we extracted?
169 if len(parts) > 0 {
170 return &telegramPlain{from.String(), strings.Join(parts, " ")}
171 }
172 return nil
173}
174
175func fileURL(fid, ext string) string {
176 return flagTeleimgRoot + fid + "." + ext
177}