blob: 7d234ffd0e0675f46982e43c1f9f0c084264de99 [file] [log] [blame]
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: "<q3k> 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("<sticker%s>", emoji))
}
// Mutually exclusive stuff.
switch {
case u.Message.Animation != nil:
// This message contains an animation.
a := u.Message.Animation
parts = append(parts, fmt.Sprintf("<uploaded animation: %s >\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("<uploaded file: %s >\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("<uploaded photo: %s >\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
}