package main import ( "context" "fmt" "strings" "time" tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api" "github.com/golang/glog" ) // telegramConnection runs a long-lived connection to the Telegram API to receive // updates and pipe resulting messages into telLog. func (s *server) telegramConnection(ctx context.Context) error { u := tgbotapi.NewUpdate(0) // TODO(q3k): figure out what the _fuck_ does this even mean u.Timeout = 60 updates, err := s.tel.GetUpdatesChan(u) if err != nil { return fmt.Errorf("GetUpdatesChan(%+v): %v", u, err) } glog.V(8).Infof("telegram/debug8: New update") for { select { case <-ctx.Done(): return ctx.Err() case update, ok := <-updates: if !ok { return fmt.Errorf("Updates channel closed") } // Dispatch update. switch { case update.Message != nil: glog.V(4).Infof("telegram/debug4: New message: %d", update.Message.Chat.ID) if update.Message.Chat.ID != s.groupId { glog.Infof("[ignored group %d] <%s> %v", update.Message.Chat.ID, update.Message.From, update.Message.Text) continue } date := time.Unix(int64(update.Message.Date), 0) if time.Since(date) > 2*time.Minute { glog.Infof("[old message] <%s> %v", update.Message.From, update.Message.Text) continue } if msg := plainFromTelegram(s.tel.Self.ID, &update); msg != nil { s.telLog <- msg } } } } } // telegramLoop maintains a telegramConnection. func (s *server) telegramLoop(ctx context.Context) { for { glog.V(4).Info("telegram/debug4: Starting telegram connection loop") err := s.telegramConnection(ctx) if err == ctx.Err() { glog.Infof("Telegram connection closing: %v", err) return } glog.Errorf("Telegram connection error: %v", err) select { case <-ctx.Done(): return case <-time.After(1 * time.Second): continue } } } // plainFromTelegram turns a Telegram message into a plain text message. func plainFromTelegram(selfID int, u *tgbotapi.Update) *telegramPlain { parts := []string{} from := u.Message.From replyto := u.Message.ReplyToMessage text := u.Message.Text // This message is in reply to someone. if replyto != nil && text != "" && replyto.From != nil { // The rendered name of the author of the quote. ruid := "@" + replyto.From.String() // First line of the quoted text. quotedLine := "" // Check if the quoted message is from our bridge. if replyto.From.ID == selfID { // Someone replied to an IRC bridge message, extract nick and line from there // eg: " foo bar baz" -> ruid = q3k; quotedLine = foo bar baz t := replyto.Text if strings.HasPrefix(t, "<") { p := strings.SplitN(t[1:], ">", 2) nick := p[0] quoted := strings.TrimSpace(p[1]) // ensure nick looks sane if len(nick) < 16 && len(strings.Fields(nick)) == 1 { quotedLine = strings.TrimSpace(strings.Split(quoted, "\n")[0]) ruid = nick } } } else { // Someone replied to a native telegram message. quoted := strings.TrimSpace(replyto.Text) quotedLine = strings.TrimSpace(strings.Split(quoted, "\n")[0]) } // If we have a line, quote it. Otherwise just refer to the nick without a quote. if quotedLine != "" { parts = append(parts, fmt.Sprintf("%s: >%s\n", ruid, quotedLine)) } else { parts = append(parts, fmt.Sprintf("%s: ", ruid)) } } // This message contains a sticker. if u.Message.Sticker != nil { emoji := "" if u.Message.Sticker.SetName != "" { emoji += "/" + u.Message.Sticker.SetName } if u.Message.Sticker.Emoji != "" { emoji += "/" + u.Message.Sticker.Emoji } parts = append(parts, fmt.Sprintf("", emoji)) } // Mutually exclusive stuff. switch { case u.Message.Animation != nil: // This message contains an animation. a := u.Message.Animation parts = append(parts, fmt.Sprintf("\n", fileURL(a.FileID, "mp4"))) case u.Message.Document != nil: // This message contains a document. d := u.Message.Document fnp := strings.Split(d.FileName, ".") ext := "bin" if len(fnp) > 1 { ext = fnp[len(fnp)-1] } parts = append(parts, fmt.Sprintf("\n", fileURL(d.FileID, ext))) case u.Message.Photo != nil: // This message contains a photo. // Multiple entries are for different file sizes, choose the highest quality one. hq := (*u.Message.Photo)[0] for _, p := range *u.Message.Photo { if p.FileSize > hq.FileSize { hq = p } } parts = append(parts, fmt.Sprintf("\n", fileURL(hq.FileID, "jpg"))) } // This message has some plain text. if text != "" { parts = append(parts, text) } // Was there anything that we extracted? if len(parts) > 0 { return &telegramPlain{from.String(), strings.Join(parts, " ")} } return nil } func fileURL(fid, ext string) string { return flagTeleimgRoot + fid + "." + ext }