mirror of https://github.com/hrfee/jfa-go
When signing up, the user is given a pin code which they send to a telegram bot. This provides user verification, but more importantly allows the bot to message the user, as the Telegram API requires the user to interact with the bot before it can do the opposite. The bot should recognize the correct language, but a /lang command is also provided to change it. The verification process is pretty much functional but ui is still broken, and it isn't properly integrated yet.pull/97/head
parent
0e21942cd6
commit
99875b9176
@ -0,0 +1,201 @@
|
||||
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)
|
||||
}
|
Loading…
Reference in new issue