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