gottdad/bot.go

501 lines
12 KiB
Go
Raw Normal View History

2020-06-21 19:51:33 +02:00
package main
import (
2021-11-13 03:40:43 +01:00
"crypto/rand"
"encoding/hex"
2020-06-21 19:54:07 +02:00
"encoding/json"
2021-11-06 16:33:16 +01:00
"fmt"
"regexp"
"strconv"
"time"
2020-06-21 19:54:07 +02:00
2020-06-21 19:51:33 +02:00
tb "gopkg.in/tucnak/telebot.v2"
)
2021-11-06 16:33:16 +01:00
type Bot struct {
bot *tb.Bot
Config *TelegramConfig
}
2021-11-13 03:40:43 +01:00
type CompanyDeleteEntry struct {
CompanyID uint8
UserID int
Time time.Time
}
var companyDeleteMap map[string]*CompanyDeleteEntry
2021-11-06 16:33:16 +01:00
func (b *Bot) Start() {
var err error
b.bot, err = tb.NewBot(tb.Settings{
Token: b.Config.Token,
URL: b.Config.URL,
Poller: &tb.LongPoller{Timeout: 10 * time.Second},
})
failError(err, "Bot.Start() : registering bot")
2021-11-13 03:40:43 +01:00
companyDeleteMap = make(map[string]*CompanyDeleteEntry)
2021-11-06 16:33:16 +01:00
b.BotHandlers()
}
func (b *Bot) SendUser(id int64, msg string) {
u := tb.User{
ID: int(id),
}
_, err := b.bot.Send(&u, msg)
logErrorDebug(err, "Bot.SendUser()")
}
func (b *Bot) SendChat(chatID int64, text string) {
opt := tb.SendOptions{
ParseMode: tb.ModeDefault,
}
ch := tb.Chat{
ID: chatID,
}
_, err := b.bot.Send(&ch, text, &opt)
logErrorDebug(err, "Bot.SendChat()")
}
2020-06-21 19:51:33 +02:00
2021-11-06 16:33:16 +01:00
func (b *Bot) BotHandlers() {
2020-06-22 11:02:04 +02:00
2021-11-06 16:33:16 +01:00
b.bot.Handle("/pause", botPause)
b.bot.Handle("/unpause", botUnpause)
b.bot.Handle("/register", botRegister)
2021-11-09 06:24:42 +01:00
b.bot.Handle("/deregister", botDeregister)
2021-11-06 16:33:16 +01:00
b.bot.Handle("/delete", botDelete)
b.bot.Handle("/companies", botCompanies)
b.bot.Handle("/clients", botClients)
b.bot.Handle("/players", botPlayers)
2021-11-09 10:45:01 +01:00
b.bot.Handle("/give", botGive)
b.bot.Handle("/take", botTake)
2021-11-10 05:56:53 +01:00
b.bot.Handle("/passwd", botPasswd)
2020-06-21 19:51:33 +02:00
2021-11-06 16:33:16 +01:00
b.bot.Handle(tb.OnPhoto, botPhoto)
b.bot.Handle(tb.OnChannelPost, botChannelPost)
b.bot.Handle(tb.OnQuery, botQuery)
b.bot.Handle(tb.OnText, botText)
b.bot.Handle(tb.OnDocument, botDocument)
go func() {
time.Sleep(time.Second)
b.SendUser(b.Config.AdminID, fmt.Sprintf("Started (%s)", version))
}()
b.bot.Start()
2020-06-21 19:51:33 +02:00
}
2020-06-22 11:02:04 +02:00
func botPause(m *tb.Message) {
2021-11-06 16:33:16 +01:00
for userID, cc := range cfg.Clients {
if userID == m.Sender.ID {
if co, ok := srv.Status.Companies[cc.CompanyID]; ok {
if clt, ok2 := srv.Status.Clients[co.ClientID]; ok2 {
clt.Paused = true
2021-11-08 07:27:49 +01:00
if !srv.Status.Paused {
srv.Pause()
} else {
bot.SendChat(bot.Config.ChatID, "Game already paused.")
}
return
2021-11-06 16:33:16 +01:00
}
}
2020-06-22 11:13:07 +02:00
}
2021-11-06 16:33:16 +01:00
}
2021-11-08 07:27:49 +01:00
bot.SendChat(bot.Config.ChatID, "You are not playing and cannot pause the game.")
2020-06-22 11:02:04 +02:00
return
}
func botUnpause(m *tb.Message) {
2021-11-06 16:33:16 +01:00
PrintText(m)
for userID, cc := range cfg.Clients {
if userID == m.Sender.ID {
if co, ok := srv.Status.Companies[cc.CompanyID]; ok {
if clt, ok2 := srv.Status.Clients[co.ClientID]; ok2 {
clt.Paused = false
2021-11-08 07:27:49 +01:00
if !srv.NeedPause() {
srv.Unpause()
} else {
bot.SendChat(bot.Config.ChatID, fmt.Sprintf("Cannot unpause : %s", srv.NeedPauseReason()))
}
return
2021-11-06 16:33:16 +01:00
}
}
2020-06-22 11:09:11 +02:00
}
2021-11-06 16:33:16 +01:00
}
2021-11-08 07:27:49 +01:00
bot.SendChat(bot.Config.ChatID, "You are not playing and cannot unpause the game.")
2021-11-06 16:33:16 +01:00
return
}
func botDelete(m *tb.Message) {
logInfoDebug("[%d] %s(%d) | %s(%d) : delete : %s\n", m.ID, m.Chat.Title, m.Chat.ID, m.Sender.Username, m.Sender.ID, m.Text)
2021-11-13 03:40:43 +01:00
r := regexp.MustCompile("/delete (?P<CompanyID>[0-9]+)")
id := uint8(255)
if r.MatchString(m.Text) {
2021-11-06 16:33:16 +01:00
ID64, _ := strconv.ParseInt(r.ReplaceAllString(m.Text, "${CompanyID}"), 10, 64)
2021-11-13 03:40:43 +01:00
if m.Sender.ID == int(bot.Config.AdminID) {
id = uint8(ID64)
} else if cc, ok := cfg.Clients[m.Sender.ID]; ok && cc.CompanyID == uint8(ID64) {
id = cc.CompanyID
2021-11-12 02:17:25 +01:00
} else {
2021-11-13 03:40:43 +01:00
bot.SendChat(m.Chat.ID, "Not authorized to delete")
return
2021-11-12 02:17:25 +01:00
}
2021-11-13 03:40:43 +01:00
} else if cc, ok := cfg.Clients[m.Sender.ID]; ok {
id = cc.CompanyID
2020-06-22 11:13:07 +02:00
} else {
2021-11-13 03:40:43 +01:00
bot.SendChat(m.Chat.ID, "User not registered")
return
}
if id == 255 {
bot.SendChat(m.Chat.ID, "No company registered")
return
}
if co, ok := srv.Status.Companies[id]; ok {
b := make([]byte, 8)
_, err := rand.Read(b)
logErrorDebug(err, "botDelete : rand.Read")
if err != nil {
bot.SendChat(m.Chat.ID, "internal error")
return
}
c := hex.EncodeToString(b)
d := &CompanyDeleteEntry{
CompanyID: id,
UserID: m.Sender.ID,
Time: time.Now(),
}
companyDeleteMap[c] = d
bot.SendChat(m.Chat.ID, fmt.Sprintf("Press /delete_%s to delete '%s'", c, co.Name))
} else {
bot.SendChat(m.Chat.ID, "Company doesn't exist")
2021-11-06 16:33:16 +01:00
}
return
}
func botCompanies(m *tb.Message) {
str := "Companies :"
for k, v := range srv.Status.Companies {
2021-11-10 07:55:31 +01:00
str = str + "\r\n" + fmt.Sprintf(" - %s (%d - %t)", v.Name, k, v.Protected)
2021-11-06 16:33:16 +01:00
}
bot.SendChat(m.Chat.ID, str)
}
func botClients(m *tb.Message) {
str := "Clients :"
for k, v := range srv.Status.Clients {
str = str + "\r\n" + fmt.Sprintf(" - %s (%d) : company #%d", v.Name, k, v.CompanyID)
}
bot.SendChat(m.Chat.ID, str)
}
func botPlayers(m *tb.Message) {
2021-11-08 15:24:34 +01:00
d1 := time.Now().Sub(cfg.Game.StartDate)
days := int(time.Duration(d1.Hours()) / 24)
d2 := time.Duration(days+1)*(time.Hour)*24 - d1
str := fmt.Sprintf("Update in %s\r\n", d2)
2021-11-08 14:51:21 +01:00
2021-11-06 16:33:16 +01:00
online := ""
for _, cc := range cfg.Clients {
if cc.Online {
2021-11-09 10:45:01 +01:00
online = online + fmt.Sprintf(" - %s (%s) : %s", cc.Username, cc.TimeLeft, srv.Status.Companies[cc.CompanyID].Name) + "\r\n"
2021-11-06 16:33:16 +01:00
}
}
offline := ""
for _, cc := range cfg.Clients {
2021-11-08 15:50:00 +01:00
if _, ok := srv.Status.Companies[cc.CompanyID]; ok && !cc.Online {
2021-11-09 10:45:01 +01:00
offline = offline + fmt.Sprintf(" - %s (%s) : %s", cc.Username, cc.TimeLeft, srv.Status.Companies[cc.CompanyID].Name) + "\r\n"
2021-11-06 16:33:16 +01:00
}
}
2021-11-08 14:51:21 +01:00
2021-11-06 16:33:16 +01:00
if len(online) > 0 {
str = str + "Players online :\r\n" + online
}
if len(offline) > 0 {
str = str + "Players offline :\r\n" + offline
}
2021-11-08 14:51:21 +01:00
2021-11-06 16:33:16 +01:00
bot.SendChat(m.Chat.ID, str)
}
2021-11-09 10:45:01 +01:00
func botGive(m *tb.Message) {
2021-11-13 03:40:43 +01:00
if m.Sender.ID != int(cfg.Telegram.AdminID) {
bot.SendChat(m.Chat.ID, "Only the admin can use this command.")
return
}
r := regexp.MustCompile("^/give @(?P<Username>[a-zA-Z0-9]+) (?P<Duration>[a-z0-9]+)")
if !r.MatchString(m.Text) {
bot.SendChat(m.Chat.ID, "Wrong format.")
return
}
uStr := r.ReplaceAllString(m.Text, "${Username}")
var u int
for ccID, cc := range cfg.Clients {
if cc.Username == uStr {
u = ccID
}
}
if u == 0 {
bot.SendChat(m.Chat.ID, "No such user found.")
return
}
dStr := r.ReplaceAllString(m.Text, "${Duration}")
d, err := time.ParseDuration(dStr)
logErrorDebug(err, "botGive : time.ParseDuration")
if err != nil {
bot.SendChat(m.Chat.ID, "Cannot parse duration.")
}
cc := cfg.Clients[u]
cc.TimeLeft += d
bot.SendChat(m.Chat.ID, fmt.Sprintf("@%s now has %s left.", uStr, cc.TimeLeft))
2021-11-09 10:45:01 +01:00
}
func botTake(m *tb.Message) {
2021-11-13 03:40:43 +01:00
if m.Sender.ID != int(cfg.Telegram.AdminID) {
bot.SendChat(m.Chat.ID, "Only the admin can use this command.")
return
}
2021-11-13 03:45:18 +01:00
r := regexp.MustCompile("^/take @(?P<Username>[a-zA-Z0-9]+) (?P<Duration>[a-z0-9]+)")
2021-11-13 03:40:43 +01:00
if !r.MatchString(m.Text) {
bot.SendChat(m.Chat.ID, "Wrong format.")
return
}
uStr := r.ReplaceAllString(m.Text, "${Username}")
var u int
for ccID, cc := range cfg.Clients {
if cc.Username == uStr {
u = ccID
}
}
if u == 0 {
bot.SendChat(m.Chat.ID, "No such user found.")
return
}
dStr := r.ReplaceAllString(m.Text, "${Duration}")
d, err := time.ParseDuration(dStr)
logErrorDebug(err, "botGive : time.ParseDuration")
if err != nil {
bot.SendChat(m.Chat.ID, "Cannot parse duration.")
}
cc := cfg.Clients[u]
cc.TimeLeft -= d
bot.SendChat(m.Chat.ID, fmt.Sprintf("@%s now has %s left.", uStr, cc.TimeLeft))
2021-11-09 10:45:01 +01:00
}
2021-11-10 05:56:53 +01:00
func botPasswd(m *tb.Message) {
cc, ok := cfg.Clients[m.Sender.ID]
if !ok {
bot.SendChat(m.Chat.ID, "User not registered.")
return
}
2021-11-10 06:41:37 +01:00
if cc.CompanyID == 255 {
bot.SendChat(m.Chat.ID, "No company registered.")
return
}
co, ok := srv.Status.Companies[cc.CompanyID]
if !ok {
bot.SendChat(m.Chat.ID, "Registered company doesn't exist.")
cc.CompanyID = 255
return
}
2021-11-10 06:02:52 +01:00
r := regexp.MustCompile("^\\/passwd( )+(?P<Passwd>[^ ]+)$")
2021-11-10 06:41:37 +01:00
if !r.MatchString(m.Text) {
2021-11-10 05:56:53 +01:00
bot.SendChat(m.Chat.ID, "No passwd provided")
2021-11-10 06:41:37 +01:00
return
2021-11-10 05:56:53 +01:00
}
2021-11-10 06:41:37 +01:00
// we have a parameter
cc.Passwd = r.ReplaceAllString(m.Text, "${Passwd}")
err := bot.bot.Delete(m)
logErrorDebug(err, "botPasswd : Delete")
2021-11-10 07:43:55 +01:00
srv.SetPasswd(co.CompanyExtlID, cc.Passwd)
2021-11-10 06:41:37 +01:00
bot.SendUser(int64(m.Sender.ID), fmt.Sprintf("Passwd set to \"%s\"", cc.Passwd))
2021-11-10 05:56:53 +01:00
return
}
2021-11-09 06:24:42 +01:00
func botDeregister(m *tb.Message) {
cc, ok := cfg.Clients[m.Sender.ID]
if !ok {
cc = &ClientConfig{
UserID: m.Sender.ID,
Username: m.Sender.Username,
CompanyID: 255,
TimeLeft: 0,
}
cfg.Clients[m.Sender.ID] = cc
bot.SendChat(m.Chat.ID, "User isn't registered.")
return
}
if cc.CompanyID != 255 {
for coID, co := range srv.Status.Companies {
if coID == cc.CompanyID {
cc.CompanyID = 255
bot.SendChat(m.Chat.ID, fmt.Sprintf("Deregistered from %s. %s playable left.", co.Name, cc.TimeLeft))
return
}
}
logInfoAlert("botRegister : %s : no such CompanyID : %d", cc.Username, cc.CompanyID)
cc.CompanyID = 255
bot.SendChat(m.Chat.ID, fmt.Sprintf("Registered company didn't exist anymore. %s playable left.", cc.TimeLeft))
return
}
return
}
2021-11-06 16:33:16 +01:00
func botRegister(m *tb.Message) {
cc, ok := cfg.Clients[m.Sender.ID]
if !ok {
cc = &ClientConfig{
UserID: m.Sender.ID,
Username: m.Sender.Username,
CompanyID: 255,
2021-11-09 06:24:42 +01:00
TimeLeft: 0,
2021-11-06 16:33:16 +01:00
}
cfg.Clients[m.Sender.ID] = cc
2021-11-09 06:24:42 +01:00
} else {
if cc.CompanyID != 255 {
for coID, co := range srv.Status.Companies {
if coID == cc.CompanyID {
bot.SendChat(m.Chat.ID, fmt.Sprintf("Already registered %s. Please /deregister first.", co.Name))
return
}
}
logInfoAlert("botRegister : %s : no such CompanyID : %d", cc.Username, cc.CompanyID)
cc.CompanyID = 255
}
2021-11-06 16:33:16 +01:00
}
2021-11-09 06:24:42 +01:00
2021-11-06 16:33:16 +01:00
coList := make(map[uint8]struct{})
for coID, _ := range srv.Status.Companies {
coList[coID] = struct{}{}
2020-06-22 11:09:11 +02:00
}
2021-11-06 16:33:16 +01:00
for _, c := range cfg.Clients {
if c.CompanyID != 255 {
if _, ok := coList[c.CompanyID]; !ok {
logInfoAlert("botRegister : %s : no such CompanyID : %d", c.Username, c.CompanyID)
c.CompanyID = 255
} else {
delete(coList, c.CompanyID)
}
}
}
2021-11-09 06:24:42 +01:00
2021-11-06 16:33:16 +01:00
if len(coList) == 0 {
bot.SendChat(m.Chat.ID, "No company to register")
return
}
2021-11-09 06:24:42 +01:00
r := regexp.MustCompile("^\\/register( )+(?P<Company>[a-zA-Z\\.\\-0-9 ]+)$")
if r.MatchString(m.Text) {
// we have a parameter
coName := r.ReplaceAllString(m.Text, "${Company}")
for coID, co := range srv.Status.Companies {
if co.Name == coName {
for _, c := range cfg.Clients {
if c.CompanyID == coID {
bot.SendChat(m.Chat.ID, fmt.Sprintf("Company %s is already registered to @%s", coName, c.Username))
return
}
}
cc.CompanyID = coID
if cc.TimeLeft == 0 {
days := int(time.Now().Sub(cfg.Game.StartDate).Hours() / 24)
cc.TimeLeft = cfg.Game.StartingAllotment + cfg.Game.DailyAllotment*time.Duration(days)
}
bot.SendChat(m.Chat.ID, fmt.Sprintf("@%s registered %s (with %s playable)", cc.Username, srv.Status.Companies[cc.CompanyID].Name, cc.TimeLeft))
return
}
}
bot.SendChat(m.Chat.ID, fmt.Sprintf("Can't find company %s", coName))
return
}
2021-11-06 16:33:16 +01:00
if len(coList) == 1 {
for id, _ := range coList {
cc.CompanyID = id
2021-11-09 06:24:42 +01:00
if cc.TimeLeft == 0 {
days := int(time.Now().Sub(cfg.Game.StartDate).Hours() / 24)
cc.TimeLeft = cfg.Game.StartingAllotment + cfg.Game.DailyAllotment*time.Duration(days)
}
2021-11-06 16:33:16 +01:00
bot.SendChat(m.Chat.ID, fmt.Sprintf("@%s registered %s (with %s playable)", cc.Username, srv.Status.Companies[cc.CompanyID].Name, cc.TimeLeft))
return
}
}
bot.SendChat(m.Chat.ID, "More than one company unregistered. Wait for bot update (poke @tiennou)")
2020-06-22 11:02:04 +02:00
return
}
2020-06-21 19:51:33 +02:00
func PrintText(m *tb.Message) {
logInfoDebug("[%d] %s(%d) | %s(%d) : %s\n", m.ID, m.Chat.Title, m.Chat.ID, m.Sender.Username, m.Sender.ID, m.Text)
return
}
func botPhoto(m *tb.Message) {
logInfoDebug("botPhoto :", m.Text)
// photos only
}
func botDocument(m *tb.Message) {
logInfoDebug("botDocument : %s (%d bytes)\n", m.Document.FileName, m.Document.File.FileSize)
// documents only
}
func botChannelPost(m *tb.Message) {
PrintText(m)
b, _ := json.Marshal(m)
2020-06-21 19:54:07 +02:00
logInfoDebug("botChannelPost : %s\n", string(b))
2020-06-21 19:51:33 +02:00
// channel posts only
}
func botQuery(q *tb.Query) {
logInfoDebug("botQuery")
// incoming inline queries
}
func botText(m *tb.Message) {
2021-11-13 03:47:54 +01:00
r := regexp.MustCompile("^/delete_(?<Ref>[a-f0-9]{16})$")
2021-11-13 03:40:43 +01:00
if r.MatchString(m.Text) {
ref := r.ReplaceAllString(m.Text, "${Ref}")
d, ok := companyDeleteMap[ref]
if !ok {
bot.SendChat(m.Chat.ID, "No corresponding deletion request.")
return
}
if d.UserID != m.Sender.ID {
bot.SendChat(m.Chat.ID, "Requesting user has to confirm himself.")
return
}
if time.Now().Sub(d.Time) > time.Minute {
bot.SendChat(m.Chat.ID, "Request expired.")
return
}
srv.DeleteCompany(d.CompanyID)
bot.SendChat(m.Chat.ID, "Company deleted.")
delete(companyDeleteMap, ref)
return
}
2020-06-21 19:51:33 +02:00
PrintText(m)
}