jfa-go/telegram.go

202 lines
5.4 KiB
Go

package main
import (
"fmt"
"math/rand"
"strconv"
"strings"
"time"
tg "github.com/go-telegram-bot-api/telegram-bot-api"
)
type TelegramDaemon struct {
Stopped bool
ShutdownChannel chan string
bot *tg.BotAPI
username string
tokens []string
verifiedTokens []string
link string
app *appContext
}
func newTelegramDaemon(app *appContext) (*TelegramDaemon, error) {
token := app.config.Section("telegram").Key("token").String()
if token == "" {
return nil, fmt.Errorf("token was blank")
}
bot, err := tg.NewBotAPI(token)
if err != nil {
return nil, err
}
return &TelegramDaemon{
Stopped: false,
ShutdownChannel: make(chan string),
bot: bot,
username: bot.Self.UserName,
tokens: []string{},
verifiedTokens: []string{},
link: "https://t.me/" + bot.Self.UserName,
app: app,
}, nil
}
var runes = []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
// NewAuthToken generates an 8-character pin in the form "A1-2B-CD".
func (t *TelegramDaemon) NewAuthToken() string {
rand.Seed(time.Now().UnixNano())
pin := make([]rune, 8)
for i := range pin {
if i == 2 || i == 5 {
pin[i] = '-'
} else {
pin[i] = runes[rand.Intn(len(runes))]
}
}
t.tokens = append(t.tokens, string(pin))
return string(pin)
}
func (t *TelegramDaemon) run() {
t.app.info.Println("Starting Telegram bot daemon")
u := tg.NewUpdate(0)
u.Timeout = 60
updates, err := t.bot.GetUpdatesChan(u)
if err != nil {
t.app.err.Printf("Failed to start Telegram daemon: %v", err)
return
}
for {
var upd tg.Update
select {
case upd = <-updates:
if upd.Message == nil {
continue
}
sects := strings.Split(upd.Message.Text, " ")
if len(sects) == 0 {
continue
}
lang := t.app.storage.lang.chosenTelegramLang
user, ok := t.app.storage.telegram[upd.Message.Chat.ID]
if !ok {
user := TelegramUser{
Username: upd.Message.Chat.UserName,
ChatID: upd.Message.Chat.ID,
Lang: "",
}
t.app.storage.telegram[upd.Message.Chat.ID] = user
err := t.app.storage.storeTelegramUsers()
if err != nil {
t.app.err.Printf("Failed to store Telegram users: %v", err)
}
}
if user.Lang != "" {
lang = user.Lang
} else {
for code := range t.app.storage.lang.Telegram {
if code[:2] == upd.Message.From.LanguageCode {
lang = code
break
}
}
}
switch msg := sects[0]; msg {
case "/start":
content := t.app.storage.lang.Telegram[lang].Strings.get("startMessage") + "\n"
content += t.app.storage.lang.Telegram[lang].Strings.get("languageMessage")
err := t.Reply(&upd, content)
if err != nil {
t.app.err.Printf("Telegram: Failed to send message to \"%s\": %v", upd.Message.From.UserName, err)
}
continue
case "/lang":
if len(sects) == 1 {
list := "/lang <lang>\n"
for code := range t.app.storage.lang.Telegram {
list += fmt.Sprintf("%s: %s\n", code, t.app.storage.lang.Telegram[code].Meta.Name)
}
err := t.Reply(&upd, list)
if err != nil {
t.app.err.Printf("Telegram: Failed to send message to \"%s\": %v", upd.Message.From.UserName, err)
}
continue
}
if _, ok := t.app.storage.lang.Telegram[sects[1]]; ok {
user.Lang = sects[1]
t.app.storage.telegram[upd.Message.Chat.ID] = user
err := t.app.storage.storeTelegramUsers()
if err != nil {
t.app.err.Printf("Failed to store Telegram users: %v", err)
}
}
continue
default:
tokenIndex := -1
for i, token := range t.tokens {
if upd.Message.Text == token {
tokenIndex = i
break
}
}
if tokenIndex == -1 {
err := t.QuoteReply(&upd, t.app.storage.lang.Telegram[lang].Strings.get("invalidPIN"))
if err != nil {
t.app.err.Printf("Telegram: Failed to send message to \"%s\": %v", upd.Message.From.UserName, err)
}
continue
}
err := t.QuoteReply(&upd, t.app.storage.lang.Telegram[lang].Strings.get("success"))
if err != nil {
t.app.err.Printf("Telegram: Failed to send message to \"%s\": %v", upd.Message.From.UserName, err)
}
t.verifiedTokens = append(t.verifiedTokens, upd.Message.Text)
t.tokens[len(t.tokens)-1], t.tokens[tokenIndex] = t.tokens[tokenIndex], t.tokens[len(t.tokens)-1]
t.tokens = t.tokens[:len(t.tokens)-1]
}
case <-t.ShutdownChannel:
t.ShutdownChannel <- "Down"
return
}
}
}
func (t *TelegramDaemon) Reply(upd *tg.Update, content string) error {
msg := tg.NewMessage((*upd).Message.Chat.ID, content)
_, err := t.bot.Send(msg)
return err
}
func (t *TelegramDaemon) QuoteReply(upd *tg.Update, content string) error {
msg := tg.NewMessage((*upd).Message.Chat.ID, content)
msg.ReplyToMessageID = (*upd).Message.MessageID
_, err := t.bot.Send(msg)
return err
}
// Send adds compatibility with EmailClient, fromName/fromAddr are discarded, message.Text is used, addresses are Chat IDs as strings.
func (t *TelegramDaemon) Send(fromName, fromAddr string, message *Message, address ...string) error {
for _, addr := range address {
ChatID, err := strconv.ParseInt(addr, 10, 64)
if err != nil {
return err
}
msg := tg.NewMessage(ChatID, message.Text)
_, err = t.bot.Send(msg)
if err != nil {
return err
}
}
return nil
}
func (t *TelegramDaemon) Shutdown() {
t.Stopped = true
t.ShutdownChannel <- "Down"
<-t.ShutdownChannel
close(t.ShutdownChannel)
}