blob: e6b673c1479f7ad57f182439d1e5fce38d689b87 [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 }
24
25 for {
26 select {
27 case <-ctx.Done():
28 return ctx.Err()
29 case u, ok := <-updates:
30 if !ok {
31 return fmt.Errorf("Updates channel closed")
32 }
33
34 // Dispatch update.
35 switch {
36 case u.Message != nil:
37 if u.Message.Chat.ID != s.groupId {
38 glog.Infof("[ignored group %d] <%s> %v", u.Message.Chat.ID, u.Message.From, u.Message.Text)
39 continue
40 }
Sergiusz Bazanski83e26902020-01-23 14:18:25 +010041 date := time.Unix(int64(u.Message.Date), 0)
42 if time.Since(date) > 2*time.Minute {
43 glog.Infof("[old message] <%s> %v", u.Message.From, u.Message.Text)
44 continue
45 }
Sergiusz Bazanskia8854882020-01-05 00:34:38 +010046 if msg := plainFromTelegram(s.tel.Self.ID, &u); msg != nil {
47 s.telLog <- msg
48 }
49 }
50 }
51 }
52}
53
54// telegramLoop maintains a telegramConnection.
55func (s *server) telegramLoop(ctx context.Context) {
56 for {
57 err := s.telegramConnection(ctx)
58 if err == ctx.Err() {
59 glog.Infof("Telegram connection closing: %v", err)
60 return
61 }
62
63 glog.Errorf("Telegram connection error: %v", err)
64 select {
65 case <-ctx.Done():
66 return
67 case <-time.After(1 * time.Second):
68 continue
69 }
70 }
71}
72
73// plainFromTelegram turns a Telegram message into a plain text message.
74func plainFromTelegram(selfID int, u *tgbotapi.Update) *telegramPlain {
75 parts := []string{}
76
77 from := u.Message.From
78 replyto := u.Message.ReplyToMessage
79 text := u.Message.Text
80
81 // This message is in reply to someone.
82 if replyto != nil && text != "" && replyto.From != nil {
83 // The rendered name of the author of the quote.
84 ruid := "@" + replyto.From.String()
85
86 // First line of the quoted text.
87 quotedLine := ""
88
89 // Check if the quoted message is from our bridge.
90 if replyto.From.ID == selfID {
91 // Someone replied to an IRC bridge message, extract nick and line from there
92 // eg: "<q3k> foo bar baz" -> ruid = q3k; quotedLine = foo bar baz
93 t := replyto.Text
94 if strings.HasPrefix(t, "<") {
95 p := strings.SplitN(t[1:], ">", 2)
96 nick := p[0]
97 quoted := strings.TrimSpace(p[1])
98
99 // ensure nick looks sane
100 if len(nick) < 16 && len(strings.Fields(nick)) == 1 {
101 quotedLine = strings.TrimSpace(strings.Split(quoted, "\n")[0])
102 ruid = nick
103 }
104 }
105 } else {
106 // Someone replied to a native telegram message.
107 quoted := strings.TrimSpace(replyto.Text)
108 quotedLine = strings.TrimSpace(strings.Split(quoted, "\n")[0])
109 }
110
111 // If we have a line, quote it. Otherwise just refer to the nick without a quote.
112 if quotedLine != "" {
113 parts = append(parts, fmt.Sprintf("%s: >%s\n", ruid, quotedLine))
114 } else {
115 parts = append(parts, fmt.Sprintf("%s: ", ruid))
116 }
117 }
118
119 // This message contains a sticker.
120 if u.Message.Sticker != nil {
121 emoji := ""
122 if u.Message.Sticker.SetName != "" {
123 emoji += "/" + u.Message.Sticker.SetName
124 }
125 if u.Message.Sticker.Emoji != "" {
126 emoji += "/" + u.Message.Sticker.Emoji
127 }
128 parts = append(parts, fmt.Sprintf("<sticker%s>", emoji))
129 }
130
Sergiusz Bazanskia07688f2020-01-05 21:21:07 +0100131 // Mutually exclusive stuff.
132 switch {
133 case u.Message.Animation != nil:
134 // This message contains an animation.
Sergiusz Bazanskia8854882020-01-05 00:34:38 +0100135 a := u.Message.Animation
136 parts = append(parts, fmt.Sprintf("<uploaded animation: %s >\n", fileURL(a.FileID, "mp4")))
Sergiusz Bazanskia8854882020-01-05 00:34:38 +0100137
Sergiusz Bazanskia07688f2020-01-05 21:21:07 +0100138 case u.Message.Document != nil:
139 // This message contains a document.
Sergiusz Bazanskia8854882020-01-05 00:34:38 +0100140 d := u.Message.Document
141 fnp := strings.Split(d.FileName, ".")
142 ext := "bin"
143 if len(fnp) > 1 {
144 ext = fnp[len(fnp)-1]
145 }
146 parts = append(parts, fmt.Sprintf("<uploaded file: %s >\n", fileURL(d.FileID, ext)))
Sergiusz Bazanskia8854882020-01-05 00:34:38 +0100147
Sergiusz Bazanskia07688f2020-01-05 21:21:07 +0100148 case u.Message.Photo != nil:
149 // This message contains a photo.
Sergiusz Bazanskia8854882020-01-05 00:34:38 +0100150 // Multiple entries are for different file sizes, choose the highest quality one.
151 hq := (*u.Message.Photo)[0]
152 for _, p := range *u.Message.Photo {
153 if p.FileSize > hq.FileSize {
154 hq = p
155 }
156 }
157 parts = append(parts, fmt.Sprintf("<uploaded photo: %s >\n", fileURL(hq.FileID, "jpg")))
158 }
159
160 // This message has some plain text.
161 if text != "" {
162 parts = append(parts, text)
163 }
164
165 // Was there anything that we extracted?
166 if len(parts) > 0 {
167 return &telegramPlain{from.String(), strings.Join(parts, " ")}
168 }
169 return nil
170}
171
172func fileURL(fid, ext string) string {
173 return flagTeleimgRoot + fid + "." + ext
174}