1906 lines
46 KiB
Go
1906 lines
46 KiB
Go
package main
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"fmt"
|
|
"math"
|
|
"regexp"
|
|
"sort"
|
|
"strconv"
|
|
"time"
|
|
|
|
"gonum.org/v1/plot"
|
|
"gonum.org/v1/plot/plotter"
|
|
"gonum.org/v1/plot/plotutil"
|
|
"gonum.org/v1/plot/vg"
|
|
tb "gopkg.in/tucnak/telebot.v2"
|
|
)
|
|
|
|
type Bot struct {
|
|
bot *tb.Bot
|
|
Config *TelegramConfig
|
|
}
|
|
|
|
type BotActionEntry struct {
|
|
Action string
|
|
CompanyID uint8
|
|
UserID int
|
|
Time time.Time
|
|
}
|
|
|
|
var botActionMap map[string]*BotActionEntry
|
|
|
|
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")
|
|
|
|
botActionMap = make(map[string]*BotActionEntry)
|
|
|
|
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()")
|
|
}
|
|
|
|
func (b *Bot) SendChatImage(chatID int64, image string) {
|
|
opt := tb.SendOptions{
|
|
ParseMode: tb.ModeDefault,
|
|
}
|
|
|
|
ch := tb.Chat{
|
|
ID: chatID,
|
|
}
|
|
|
|
photo := &tb.Photo{File: tb.FromDisk(image)}
|
|
|
|
_, err := b.bot.Send(&ch, photo, &opt)
|
|
logErrorDebug(err, "Bot.SendChatImage()")
|
|
}
|
|
|
|
func (b *Bot) BotHandlers() {
|
|
|
|
b.bot.Handle("/pause", botPause)
|
|
b.bot.Handle("/unpause", botUnpause)
|
|
b.bot.Handle("/register", botRegister)
|
|
b.bot.Handle("/deregister", botDeregister)
|
|
b.bot.Handle("/delete", botDelete)
|
|
b.bot.Handle("/offline", botOffline)
|
|
b.bot.Handle("/companies", botCompanies)
|
|
b.bot.Handle("/clients", botClients)
|
|
|
|
b.bot.Handle("/passwd", botPasswd)
|
|
b.bot.Handle("/say", botSay)
|
|
b.bot.Handle("/help", botHelp)
|
|
|
|
b.bot.Handle("/version", botVersion)
|
|
b.bot.Handle("/save", botSave)
|
|
|
|
b.bot.Handle("/reset", botReset)
|
|
b.bot.Handle("/ready", botReady)
|
|
b.bot.Handle("/start", botStart)
|
|
b.bot.Handle("/players", botPlayers)
|
|
|
|
b.bot.Handle("/give", botGive)
|
|
b.bot.Handle("/take", botTake)
|
|
b.bot.Handle("/transfer", botTransfer)
|
|
|
|
b.bot.Handle("/value", botGraphValue)
|
|
b.bot.Handle("/value_delta", botGraphValueDelta)
|
|
b.bot.Handle("/money", botGraphMoney)
|
|
b.bot.Handle("/income", botGraphIncome)
|
|
b.bot.Handle("/loan", botGraphLoan)
|
|
b.bot.Handle("/perf", botGraphPerf)
|
|
b.bot.Handle("/planes", botGraphPlanes)
|
|
b.bot.Handle("/busses", botGraphBusses)
|
|
b.bot.Handle("/trains", botGraphTrains)
|
|
b.bot.Handle("/lorries", botGraphLorries)
|
|
b.bot.Handle("/ships", botGraphShips)
|
|
|
|
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()
|
|
}
|
|
|
|
func botHelp(m *tb.Message) {
|
|
msg := ""
|
|
msg = fmt.Sprintf("%s/pause - pause the game\r\n", msg)
|
|
msg = fmt.Sprintf("%s/unpause - unpause the game\r\n", msg)
|
|
msg = fmt.Sprintf("%s/register - register company\r\n", msg)
|
|
msg = fmt.Sprintf("%s/deregister - deregister company\r\n", msg)
|
|
msg = fmt.Sprintf("%s/delete - delete company\r\n", msg)
|
|
msg = fmt.Sprintf("%s/companies - list companies\r\n", msg)
|
|
msg = fmt.Sprintf("%s/clients - list clients\r\n", msg)
|
|
msg = fmt.Sprintf("%s/give - give time to player\r\n", msg)
|
|
msg = fmt.Sprintf("%s/take - take time from player\r\n", msg)
|
|
msg = fmt.Sprintf("%s/transfer - transfer time between players\r\n", msg)
|
|
msg = fmt.Sprintf("%s/passwd - change passwd\r\n", msg)
|
|
msg = fmt.Sprintf("%s/say - send msg to the game\r\n", msg)
|
|
msg = fmt.Sprintf("%s/reset - reset the game\r\n", msg)
|
|
msg = fmt.Sprintf("%s/start - start the game\r\n", msg)
|
|
msg = fmt.Sprintf("%s/ready - set player as ready\r\n", msg)
|
|
msg = fmt.Sprintf("%s/players - list players\r\n", msg)
|
|
msg = fmt.Sprintf("%s/offline - set player offline\r\n", msg)
|
|
msg = fmt.Sprintf("%s/value - value graph\r\n", msg)
|
|
msg = fmt.Sprintf("%s/value_delta - delta value graph\r\n", msg)
|
|
msg = fmt.Sprintf("%s/money - money graph\r\n", msg)
|
|
msg = fmt.Sprintf("%s/income - income graph\r\n", msg)
|
|
msg = fmt.Sprintf("%s/loan - loan graph\r\n", msg)
|
|
msg = fmt.Sprintf("%s/perf - perf graph\r\n", msg)
|
|
msg = fmt.Sprintf("%s/busses - busses graph\r\n", msg)
|
|
msg = fmt.Sprintf("%s/trains - trains graph\r\n", msg)
|
|
msg = fmt.Sprintf("%s/planes - planes graph\r\n", msg)
|
|
msg = fmt.Sprintf("%s/lorries - lorries graph\r\n", msg)
|
|
msg = fmt.Sprintf("%s/ships - ships graph\r\n", msg)
|
|
msg = fmt.Sprintf("%s/version - version\r\n", msg)
|
|
msg = fmt.Sprintf("%s/help - this\r\n", msg)
|
|
|
|
bot.SendChat(m.Chat.ID, msg)
|
|
return
|
|
}
|
|
|
|
func botPause(m *tb.Message) {
|
|
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
|
|
if !srv.Status.Paused {
|
|
srv.Pause()
|
|
} else {
|
|
bot.SendChat(bot.Config.ChatID, "Game already paused.")
|
|
}
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
bot.SendChat(bot.Config.ChatID, "You are not playing and cannot pause the game.")
|
|
return
|
|
}
|
|
|
|
func botUnpause(m *tb.Message) {
|
|
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
|
|
if !srv.NeedPause() {
|
|
srv.Unpause()
|
|
} else {
|
|
bot.SendChat(bot.Config.ChatID, fmt.Sprintf("Cannot unpause : %s", srv.NeedPauseReason()))
|
|
}
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
bot.SendChat(bot.Config.ChatID, "You are not playing and cannot unpause the game.")
|
|
return
|
|
}
|
|
|
|
func botOffline(m *tb.Message) {
|
|
if m.Sender.ID != int(cfg.Telegram.AdminID) {
|
|
bot.SendChat(m.Chat.ID, "Only the admin can use this command.")
|
|
return
|
|
}
|
|
|
|
r := regexp.MustCompile("^\\/offline @(?P<Username>[a-zA-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
|
|
}
|
|
|
|
cc := cfg.Clients[u]
|
|
cc.Online = false
|
|
|
|
bot.SendChat(m.Chat.ID, fmt.Sprintf("@%s is now offline.", uStr))
|
|
}
|
|
|
|
func botDelete(m *tb.Message) {
|
|
r := regexp.MustCompile("\\/delete (?P<CompanyID>[0-9]+)")
|
|
id := uint8(255)
|
|
if r.MatchString(m.Text) {
|
|
ID64, _ := strconv.ParseInt(r.ReplaceAllString(m.Text, "${CompanyID}"), 10, 64)
|
|
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
|
|
} else {
|
|
bot.SendChat(m.Chat.ID, "Not authorized to delete")
|
|
return
|
|
}
|
|
} else if cc, ok := cfg.Clients[m.Sender.ID]; ok {
|
|
id = cc.CompanyID
|
|
} else {
|
|
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 := &BotActionEntry{
|
|
Action: "DeleteCompany",
|
|
CompanyID: id,
|
|
UserID: m.Sender.ID,
|
|
Time: time.Now(),
|
|
}
|
|
botActionMap[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")
|
|
}
|
|
return
|
|
}
|
|
|
|
func botActuallyDelete(m *tb.Message) {
|
|
r := regexp.MustCompile("^\\/delete_(?P<Ref>[a-f0-9]{16})$")
|
|
ref := r.ReplaceAllString(m.Text, "${Ref}")
|
|
a, ok := botActionMap[ref]
|
|
if !ok {
|
|
bot.SendChat(m.Chat.ID, "No corresponding request.")
|
|
return
|
|
}
|
|
if a.Action != "DeleteCompany" {
|
|
bot.SendChat(m.Chat.ID, "Not a delete request.")
|
|
return
|
|
}
|
|
if a.UserID != m.Sender.ID {
|
|
bot.SendChat(m.Chat.ID, "Requesting user has to confirm himself.")
|
|
return
|
|
}
|
|
if time.Now().Sub(a.Time) > time.Minute {
|
|
bot.SendChat(m.Chat.ID, "Request expired.")
|
|
} else {
|
|
srv.DeleteCompany(a.CompanyID)
|
|
bot.SendChat(m.Chat.ID, "Company deleted.")
|
|
}
|
|
delete(botActionMap, ref)
|
|
return
|
|
|
|
}
|
|
|
|
func botCompanies(m *tb.Message) {
|
|
str := "Companies :"
|
|
for k, v := range srv.Status.Companies {
|
|
str = str + "\r\n" + fmt.Sprintf(" - %s (%d - %t)", v.Name, k, v.Protected)
|
|
}
|
|
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 botReset(m *tb.Message) {
|
|
if m.Sender.ID != int(cfg.Telegram.AdminID) {
|
|
bot.SendChat(m.Chat.ID, "Only the admin can use this command.")
|
|
return
|
|
}
|
|
|
|
if !cfg.Game.Started {
|
|
bot.SendChat(m.Chat.ID, "Game is not started.")
|
|
return
|
|
}
|
|
|
|
b := make([]byte, 8)
|
|
_, err := rand.Read(b)
|
|
logErrorDebug(err, "botReset : rand.Read")
|
|
if err != nil {
|
|
bot.SendChat(m.Chat.ID, "internal error")
|
|
return
|
|
}
|
|
|
|
c := hex.EncodeToString(b)
|
|
d := &BotActionEntry{
|
|
Action: "ResetGame",
|
|
CompanyID: 0,
|
|
UserID: m.Sender.ID,
|
|
Time: time.Now(),
|
|
}
|
|
botActionMap[c] = d
|
|
bot.SendChat(m.Chat.ID, fmt.Sprintf("Press /reset_%s to reset the game", c))
|
|
return
|
|
}
|
|
|
|
func botActuallyReset(m *tb.Message) {
|
|
r := regexp.MustCompile("^\\/reset_(?P<Ref>[a-f0-9]{16})$")
|
|
|
|
ref := r.ReplaceAllString(m.Text, "${Ref}")
|
|
a, ok := botActionMap[ref]
|
|
if !ok {
|
|
bot.SendChat(m.Chat.ID, "No corresponding request.")
|
|
return
|
|
}
|
|
if a.Action != "ResetGame" {
|
|
bot.SendChat(m.Chat.ID, "Not a reset request.")
|
|
return
|
|
}
|
|
if a.UserID != m.Sender.ID {
|
|
bot.SendChat(m.Chat.ID, "Requesting user has to confirm himself.")
|
|
return
|
|
}
|
|
if time.Now().Sub(a.Time) > time.Minute {
|
|
bot.SendChat(m.Chat.ID, "Request expired.")
|
|
} else {
|
|
cfg.Save("backup." + *configFlag)
|
|
cfg.Game.Started = false
|
|
cfg.StatsMonthly = make(map[int]map[string]*StatMonthly)
|
|
cfg.StatsDaily = make(map[int]map[string]*StatDaily)
|
|
for _, cc := range cfg.Clients {
|
|
cc.Ready = false
|
|
cc.CompanyID = 255
|
|
cc.Online = false
|
|
cc.TimeLeft = 0
|
|
}
|
|
bot.SendChat(m.Chat.ID, "Game resetted.")
|
|
}
|
|
delete(botActionMap, ref)
|
|
return
|
|
}
|
|
|
|
func botStart(m *tb.Message) {
|
|
if cfg.Game.Started {
|
|
bot.SendChat(m.Chat.ID, "Game already started.")
|
|
return
|
|
}
|
|
|
|
actuallyReady := true
|
|
for _, cc := range cfg.Clients {
|
|
if !cc.Ready {
|
|
actuallyReady = false
|
|
}
|
|
}
|
|
if m.Sender.ID != int(cfg.Telegram.AdminID) && !actuallyReady {
|
|
bot.SendChat(m.Chat.ID, "Not all players are ready. Only the admin can force the start.")
|
|
}
|
|
|
|
b := make([]byte, 8)
|
|
_, err := rand.Read(b)
|
|
logErrorDebug(err, "botStart : rand.Read")
|
|
if err != nil {
|
|
bot.SendChat(m.Chat.ID, "internal error")
|
|
return
|
|
}
|
|
|
|
c := hex.EncodeToString(b)
|
|
d := &BotActionEntry{
|
|
Action: "StartGame",
|
|
CompanyID: 0,
|
|
UserID: m.Sender.ID,
|
|
Time: time.Now(),
|
|
}
|
|
botActionMap[c] = d
|
|
bot.SendChat(m.Chat.ID, fmt.Sprintf("Press /start_%s to actually start game", c))
|
|
return
|
|
}
|
|
|
|
func botActuallyStart(m *tb.Message) {
|
|
r := regexp.MustCompile("^\\/start_(?P<Ref>[a-f0-9]{16})$")
|
|
|
|
ref := r.ReplaceAllString(m.Text, "${Ref}")
|
|
a, ok := botActionMap[ref]
|
|
if !ok {
|
|
bot.SendChat(m.Chat.ID, "No corresponding request.")
|
|
return
|
|
}
|
|
if a.Action != "StartGame" {
|
|
bot.SendChat(m.Chat.ID, "Not a game start request.")
|
|
return
|
|
}
|
|
if a.UserID != m.Sender.ID {
|
|
bot.SendChat(m.Chat.ID, "Requesting user has to confirm himself.")
|
|
return
|
|
}
|
|
if time.Now().Sub(a.Time) > time.Minute {
|
|
bot.SendChat(m.Chat.ID, "Request expired.")
|
|
} else {
|
|
cfg.Game.Started = true
|
|
cfg.Game.StartDate = time.Now()
|
|
for _, cc := range cfg.Clients {
|
|
cc.Ready = true
|
|
cc.TimeLeft = cfg.Game.StartingAllotment
|
|
}
|
|
bot.SendChat(m.Chat.ID, "Game started !")
|
|
}
|
|
delete(botActionMap, ref)
|
|
return
|
|
|
|
}
|
|
|
|
func botReady(m *tb.Message) {
|
|
if cfg.Game.Started {
|
|
bot.SendChat(m.Chat.ID, "Game is already started.")
|
|
return
|
|
}
|
|
|
|
cc, ok := cfg.Clients[m.Sender.ID]
|
|
if !ok {
|
|
bot.SendChat(m.Chat.ID, "Player not registered")
|
|
return
|
|
}
|
|
|
|
ready := 0
|
|
waiting := 0
|
|
for _, cc2 := range cfg.Clients {
|
|
if cc2.Ready {
|
|
ready++
|
|
} else {
|
|
waiting++
|
|
}
|
|
}
|
|
|
|
if cc.Ready {
|
|
cc.Ready = false
|
|
ready--
|
|
waiting++
|
|
bot.SendChat(m.Chat.ID, fmt.Sprintf("Player not ready anymore. Only %d players ready now.", ready))
|
|
} else {
|
|
cc.Ready = true
|
|
ready++
|
|
waiting--
|
|
bot.SendChat(m.Chat.ID, fmt.Sprintf("Player is now ready. Still waiting for %d players.", waiting))
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func botPlayers(m *tb.Message) {
|
|
if cfg.Game.Started {
|
|
botPlayersStarted(m)
|
|
} else {
|
|
botPlayersWaiting(m)
|
|
}
|
|
return
|
|
}
|
|
|
|
func botPlayersStarted(m *tb.Message) {
|
|
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.Round(time.Second))
|
|
|
|
online := ""
|
|
for _, cc := range cfg.Clients {
|
|
if cc.Online {
|
|
if co, ok := srv.Status.Companies[cc.CompanyID]; ok {
|
|
online = online + fmt.Sprintf(" - %s (%s) : %s", cc.Username, cc.TimeLeft.Round(time.Second), co.Name) + "\r\n"
|
|
} else {
|
|
online = online + fmt.Sprintf(" - %s (%s) : none", cc.Username, cc.TimeLeft.Round(time.Second)) + "\r\n"
|
|
}
|
|
}
|
|
}
|
|
offline := ""
|
|
for _, cc := range cfg.Clients {
|
|
if co, ok := srv.Status.Companies[cc.CompanyID]; ok && !cc.Online {
|
|
offline = offline + fmt.Sprintf(" - %s (%s) : %s", cc.Username, cc.TimeLeft.Round(time.Second), co.Name) + "\r\n"
|
|
} else {
|
|
offline = offline + fmt.Sprintf(" - %s (%s) : none", cc.Username, cc.TimeLeft.Round(time.Second)) + "\r\n"
|
|
}
|
|
}
|
|
|
|
if len(online) > 0 {
|
|
str = str + "Players online :\r\n" + online
|
|
}
|
|
if len(offline) > 0 {
|
|
str = str + "Players offline :\r\n" + offline
|
|
}
|
|
|
|
bot.SendChat(m.Chat.ID, str)
|
|
}
|
|
|
|
func botPlayersWaiting(m *tb.Message) {
|
|
msg := "Player status :\r\n"
|
|
ready := ""
|
|
waiting := ""
|
|
for _, cc := range cfg.Clients {
|
|
if cc.Ready {
|
|
ready = ready + fmt.Sprintf(" - %s", cc.Username) + "\r\n"
|
|
} else {
|
|
waiting = waiting + fmt.Sprintf(" - %s", cc.Username) + "\r\n"
|
|
}
|
|
}
|
|
|
|
if len(ready) > 0 {
|
|
msg = msg + "Players ready :\r\n" + ready
|
|
}
|
|
if len(waiting) > 0 {
|
|
msg = msg + "Waiting for :\r\n" + waiting
|
|
}
|
|
|
|
bot.SendChat(m.Chat.ID, msg)
|
|
}
|
|
|
|
func botGive(m *tb.Message) {
|
|
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.Round(time.Second)))
|
|
}
|
|
|
|
func botVersion(m *tb.Message) {
|
|
bot.SendChat(m.Chat.ID, version)
|
|
return
|
|
}
|
|
|
|
func botSave(m *tb.Message) {
|
|
if m.Sender.ID != int(cfg.Telegram.AdminID) {
|
|
bot.SendChat(m.Chat.ID, "Only the admin can use this command.")
|
|
return
|
|
}
|
|
|
|
r := regexp.MustCompile("^\\/save (?P<Filename>[a-zA-Z0-9._\\/]+)")
|
|
|
|
if !r.MatchString(m.Text) {
|
|
bot.SendChat(m.Chat.ID, "Wrong usage.")
|
|
return
|
|
}
|
|
|
|
filename := r.ReplaceAllString(m.Text, "${Filename}")
|
|
|
|
err := cfg.Save(filename + ".json")
|
|
logErrorDebug(err, "botSave : Config.Save(%s)", filename+".json")
|
|
if err != nil {
|
|
bot.SendChat(m.Chat.ID, fmt.Sprintf("Error : %s", err))
|
|
} else {
|
|
bot.SendChat(m.Chat.ID, "Saved.")
|
|
}
|
|
return
|
|
}
|
|
|
|
func botTransfer(m *tb.Message) {
|
|
r := regexp.MustCompile("^\\/transfer @(?P<Username>[a-zA-Z0-9]+) (?P<Duration>[a-z0-9]+)")
|
|
|
|
if !r.MatchString(m.Text) {
|
|
bot.SendChat(m.Chat.ID, "Wrong usage.")
|
|
return
|
|
}
|
|
|
|
ccFrom, ok := cfg.Clients[m.Sender.ID]
|
|
if !ok {
|
|
bot.SendChat(m.Chat.ID, "Client not registered.")
|
|
return
|
|
}
|
|
|
|
dStr := r.ReplaceAllString(m.Text, "${Duration}")
|
|
d, err := time.ParseDuration(dStr)
|
|
logErrorDebug(err, "botTransfer : time.ParseDuration")
|
|
if err != nil {
|
|
bot.SendChat(m.Chat.ID, "Cannot parse duration.")
|
|
return
|
|
}
|
|
|
|
if ccFrom.TimeLeft < d {
|
|
bot.SendChat(m.Chat.ID, "Not enough time left for user.")
|
|
return
|
|
}
|
|
|
|
uStr := r.ReplaceAllString(m.Text, "${Username}")
|
|
var toID int
|
|
|
|
for ccID, cc := range cfg.Clients {
|
|
if cc.Username == uStr {
|
|
toID = ccID
|
|
}
|
|
}
|
|
if toID == 0 {
|
|
bot.SendChat(m.Chat.ID, "No such user found.")
|
|
return
|
|
}
|
|
|
|
ccTo := cfg.Clients[toID]
|
|
ccTo.TimeLeft += d
|
|
ccFrom.TimeLeft -= d
|
|
|
|
bot.SendChat(m.Chat.ID, fmt.Sprintf("@%s now has %s left.\r\n@%s has %s left.", uStr, ccTo.TimeLeft.Round(time.Second), ccFrom.Username, ccFrom.TimeLeft.Round(time.Second)))
|
|
}
|
|
|
|
func botTake(m *tb.Message) {
|
|
if m.Sender.ID != int(cfg.Telegram.AdminID) {
|
|
bot.SendChat(m.Chat.ID, "Only the admin can use this command.")
|
|
return
|
|
}
|
|
|
|
r := regexp.MustCompile("^\\/take @(?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.")
|
|
return
|
|
}
|
|
|
|
cc := cfg.Clients[u]
|
|
cc.TimeLeft -= d
|
|
|
|
bot.SendChat(m.Chat.ID, fmt.Sprintf("@%s now has %s left.", uStr, cc.TimeLeft.Round(time.Second)))
|
|
}
|
|
|
|
func botSay(m *tb.Message) {
|
|
r := regexp.MustCompile("^\\/say( )+(?P<Message>.+)$")
|
|
if !r.MatchString(m.Text) {
|
|
bot.SendChat(m.Chat.ID, "No message provided")
|
|
return
|
|
}
|
|
|
|
msg := r.ReplaceAllString(m.Text, "${Message}")
|
|
|
|
px := PacketAdminRCon{
|
|
Packet: Packet{PType: AdminPacketAdminRCon},
|
|
Command: fmt.Sprintf("say \"@%s says : %s\"", m.Sender.Username, msg),
|
|
}
|
|
srv.Send(px.Bytes())
|
|
|
|
return
|
|
}
|
|
|
|
func botPasswd(m *tb.Message) {
|
|
cc, ok := cfg.Clients[m.Sender.ID]
|
|
if !ok {
|
|
bot.SendChat(m.Chat.ID, "User not registered.")
|
|
return
|
|
}
|
|
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
|
|
}
|
|
|
|
r := regexp.MustCompile("^\\/passwd( )+(?P<Passwd>[^ ]+)$")
|
|
if !r.MatchString(m.Text) {
|
|
bot.SendChat(m.Chat.ID, "No passwd provided")
|
|
return
|
|
}
|
|
// we have a parameter
|
|
cc.Passwd = r.ReplaceAllString(m.Text, "${Passwd}")
|
|
|
|
err := bot.bot.Delete(m)
|
|
logErrorDebug(err, "botPasswd : Delete")
|
|
|
|
srv.SetPasswd(co.CompanyExtlID, cc.Passwd)
|
|
|
|
bot.SendUser(int64(m.Sender.ID), fmt.Sprintf("Passwd set to \"%s\"", cc.Passwd))
|
|
|
|
return
|
|
}
|
|
|
|
func botDeregister(m *tb.Message) {
|
|
r := regexp.MustCompile("^\\/deregister @(?P<Username>[a-zA-Z0-9]+)$")
|
|
if r.MatchString(m.Text) {
|
|
if m.Sender.ID != int(cfg.Telegram.AdminID) {
|
|
bot.SendChat(m.Chat.ID, "Not admin.")
|
|
return
|
|
}
|
|
|
|
uStr := r.ReplaceAllString(m.Text, "${Username}")
|
|
var uID int
|
|
|
|
for ccID, cc := range cfg.Clients {
|
|
if cc.Username == uStr {
|
|
uID = ccID
|
|
}
|
|
}
|
|
|
|
if uID == 0 {
|
|
bot.SendChat(m.Chat.ID, "No such user found.")
|
|
return
|
|
}
|
|
|
|
cc := cfg.Clients[uID]
|
|
|
|
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.Round(time.Second)))
|
|
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.Round(time.Second)))
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
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.Round(time.Second)))
|
|
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.Round(time.Second)))
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
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,
|
|
TimeLeft: 0,
|
|
Ready: cfg.Game.Started,
|
|
}
|
|
cfg.Clients[m.Sender.ID] = cc
|
|
} 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
|
|
}
|
|
}
|
|
|
|
coList := make(map[uint8]struct{})
|
|
for coID, _ := range srv.Status.Companies {
|
|
coList[coID] = struct{}{}
|
|
}
|
|
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)
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
if len(coList) == 0 {
|
|
bot.SendChat(m.Chat.ID, "No company to register")
|
|
return
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
if len(coList) == 1 {
|
|
for id, _ := range coList {
|
|
cc.CompanyID = id
|
|
if cc.TimeLeft == 0 && cfg.Game.Started {
|
|
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.Round(time.Second)))
|
|
} else {
|
|
bot.SendChat(m.Chat.ID, fmt.Sprintf("@%s registered %s", cc.Username, srv.Status.Companies[cc.CompanyID].Name))
|
|
}
|
|
return
|
|
}
|
|
}
|
|
bot.SendChat(m.Chat.ID, "More than one company unregistered. Wait for bot update (poke @tiennou)")
|
|
return
|
|
}
|
|
|
|
func botGraphValue(m *tb.Message) {
|
|
var (
|
|
maxVal float64
|
|
unitFactor float64
|
|
unitName string
|
|
)
|
|
for _, dStats := range cfg.StatsMonthly {
|
|
for _, stat := range dStats {
|
|
valueFloat := float64(stat.CompanyValueLastQuarter)
|
|
if math.Abs(valueFloat) > maxVal {
|
|
maxVal = math.Abs(valueFloat)
|
|
}
|
|
}
|
|
}
|
|
if maxVal > 1000000000 {
|
|
unitFactor = 1000000000
|
|
unitName = "billion"
|
|
} else if maxVal > 1000000 {
|
|
unitFactor = 1000000
|
|
unitName = "million"
|
|
} else {
|
|
unitFactor = 1
|
|
unitName = ""
|
|
}
|
|
|
|
var vals map[int]plotter.XYs
|
|
vals = make(map[int]plotter.XYs)
|
|
for ccID, dStats := range cfg.StatsMonthly {
|
|
vals[ccID] = make(plotter.XYs, 0)
|
|
for dStr, stat := range dStats {
|
|
d, err := time.Parse("20060102", dStr)
|
|
logErrorDebug(err, "botGraphValue : time.Parse")
|
|
if err != nil {
|
|
bot.SendChat(m.Chat.ID, fmt.Sprintf("time.Parse : %s", err))
|
|
return
|
|
}
|
|
dateFloat := float64(d.Year()) + float64(d.Month()-1)/12
|
|
valueFloat := float64(stat.CompanyValueLastQuarter)
|
|
pt := plotter.XY{
|
|
X: dateFloat,
|
|
Y: valueFloat / unitFactor,
|
|
}
|
|
vals[ccID] = append(vals[ccID], pt)
|
|
}
|
|
sort.Slice(vals[ccID], func(i, j int) bool { return vals[ccID][i].X < vals[ccID][j].X })
|
|
}
|
|
|
|
p := plot.New()
|
|
p.Title.Text = "Company Values"
|
|
p.X.Label.Text = "Year"
|
|
if unitName != "" {
|
|
p.Y.Label.Text = fmt.Sprintf("Value (%s)", unitName)
|
|
} else {
|
|
p.Y.Label.Text = "Value"
|
|
}
|
|
|
|
i := 0
|
|
for ccID, xys := range vals {
|
|
cc := cfg.Clients[ccID]
|
|
|
|
l, s, err := plotter.NewLinePoints(xys)
|
|
logErrorDebug(err, "botGraphValue : plotter.NewLinePoints")
|
|
if err != nil {
|
|
bot.SendChat(m.Chat.ID, fmt.Sprintf("plotter.NewLinePoints : %s", err))
|
|
return
|
|
}
|
|
|
|
l.Color = plotutil.Color(i)
|
|
l.Dashes = plotutil.Dashes(2)
|
|
s.Color = plotutil.Color(i)
|
|
s.Shape = plotutil.Shape(0)
|
|
|
|
p.Add(l)
|
|
p.Add(s)
|
|
p.Legend.Add(cc.Username, l, s)
|
|
|
|
i++
|
|
}
|
|
|
|
err := p.Save(12*vg.Inch, 8*vg.Inch, "/app/data/points.png")
|
|
logErrorDebug(err, "botGraphValue : plot.Save")
|
|
if err != nil {
|
|
bot.SendChat(m.Chat.ID, fmt.Sprintf("plot.Save : %s", err))
|
|
return
|
|
}
|
|
|
|
bot.SendChatImage(m.Chat.ID, "/app/data/points.png")
|
|
|
|
return
|
|
}
|
|
|
|
func botGraphValueDelta(m *tb.Message) {
|
|
var (
|
|
maxVal float64
|
|
unitFactor float64
|
|
unitName string
|
|
)
|
|
|
|
var vals map[int]plotter.XYs
|
|
vals = make(map[int]plotter.XYs)
|
|
for ccID, dStats := range cfg.StatsMonthly {
|
|
vals[ccID] = make(plotter.XYs, 0)
|
|
for dStr, stat := range dStats {
|
|
d, err := time.Parse("20060102", dStr)
|
|
logErrorDebug(err, "botGraphValueDelta : time.Parse")
|
|
if err != nil {
|
|
bot.SendChat(m.Chat.ID, fmt.Sprintf("time.Parse : %s", err))
|
|
return
|
|
}
|
|
pt := plotter.XY{
|
|
X: float64(d.Year()) + float64(d.Month()-1)/12,
|
|
Y: float64(stat.CompanyValuePreviousQuarter),
|
|
}
|
|
vals[ccID] = append(vals[ccID], pt)
|
|
}
|
|
sort.Slice(vals[ccID], func(i, j int) bool { return vals[ccID][i].X < vals[ccID][j].X })
|
|
}
|
|
|
|
for ccID, v := range vals {
|
|
v2 := make(plotter.XYs, 0)
|
|
for i := 0; i < len(v); i = i + 3 {
|
|
v2 = append(v2, v[i])
|
|
}
|
|
vals[ccID] = v2
|
|
}
|
|
|
|
for ccID, v := range vals {
|
|
v0 := v[0].Y
|
|
for i := 1; i < len(v); i++ {
|
|
v[i].Y, v0 = v[i].Y-v0, v[i].Y
|
|
if math.Abs(v[i].Y) > maxVal {
|
|
maxVal = math.Abs(v[i].Y)
|
|
}
|
|
}
|
|
vals[ccID] = v[1:]
|
|
}
|
|
|
|
if maxVal > 1000000000 {
|
|
unitFactor = 1000000000
|
|
unitName = "billion"
|
|
} else if maxVal > 1000000 {
|
|
unitFactor = 1000000
|
|
unitName = "million"
|
|
} else {
|
|
unitFactor = 1
|
|
unitName = ""
|
|
}
|
|
|
|
for ccID, v := range vals {
|
|
for i := 0; i < len(v); i++ {
|
|
v[i].Y = v[i].Y / unitFactor
|
|
if math.IsNaN(v[i].Y) {
|
|
logInfoDebug("botGraphValueDelta : NaN : %d / %f", ccID, v[i].X)
|
|
}
|
|
}
|
|
vals[ccID] = v
|
|
}
|
|
|
|
p := plot.New()
|
|
p.Title.Text = "Company Values (delta)"
|
|
p.X.Label.Text = "Year"
|
|
if unitName != "" {
|
|
p.Y.Label.Text = fmt.Sprintf("Variation (%s)", unitName)
|
|
} else {
|
|
p.Y.Label.Text = "Variation"
|
|
}
|
|
|
|
i := 0
|
|
for ccID, xys := range vals {
|
|
cc := cfg.Clients[ccID]
|
|
|
|
l, s, err := plotter.NewLinePoints(xys)
|
|
logErrorDebug(err, "botGraphValueDelta : plotter.NewLinePoints")
|
|
if err != nil {
|
|
bot.SendChat(m.Chat.ID, fmt.Sprintf("plotter.NewLinePoints : %s", err))
|
|
return
|
|
}
|
|
|
|
l.Color = plotutil.Color(i)
|
|
l.Dashes = plotutil.Dashes(2)
|
|
s.Color = plotutil.Color(i)
|
|
s.Shape = plotutil.Shape(0)
|
|
|
|
p.Add(l)
|
|
p.Add(s)
|
|
p.Legend.Add(cc.Username, l, s)
|
|
|
|
i++
|
|
}
|
|
|
|
err := p.Save(12*vg.Inch, 8*vg.Inch, "/app/data/points.png")
|
|
logErrorDebug(err, "botGraphValueDelta : plot.Save")
|
|
if err != nil {
|
|
bot.SendChat(m.Chat.ID, fmt.Sprintf("plot.Save : %s", err))
|
|
return
|
|
}
|
|
|
|
bot.SendChatImage(m.Chat.ID, "/app/data/points.png")
|
|
|
|
return
|
|
}
|
|
|
|
func botGraphMoney(m *tb.Message) {
|
|
var (
|
|
maxVal float64
|
|
unitFactor float64
|
|
unitName string
|
|
)
|
|
for _, dStats := range cfg.StatsMonthly {
|
|
for _, stat := range dStats {
|
|
valueFloat := float64(stat.Money)
|
|
if math.Abs(valueFloat) > maxVal {
|
|
maxVal = math.Abs(valueFloat)
|
|
}
|
|
}
|
|
}
|
|
if maxVal > 1000000000 {
|
|
unitFactor = 1000000000
|
|
unitName = "billion"
|
|
} else if maxVal > 1000000 {
|
|
unitFactor = 1000000
|
|
unitName = "million"
|
|
} else {
|
|
unitFactor = 1
|
|
unitName = ""
|
|
}
|
|
|
|
var vals map[int]plotter.XYs
|
|
vals = make(map[int]plotter.XYs)
|
|
for ccID, dStats := range cfg.StatsMonthly {
|
|
vals[ccID] = make(plotter.XYs, 0)
|
|
for dStr, stat := range dStats {
|
|
d, err := time.Parse("20060102", dStr)
|
|
logErrorDebug(err, "botGraphMoney : time.Parse")
|
|
if err != nil {
|
|
bot.SendChat(m.Chat.ID, fmt.Sprintf("time.Parse : %s", err))
|
|
return
|
|
}
|
|
dateFloat := float64(d.Year()) + float64(d.Month()-1)/12
|
|
valueFloat := float64(stat.Money)
|
|
pt := plotter.XY{
|
|
X: dateFloat,
|
|
Y: valueFloat / unitFactor,
|
|
}
|
|
vals[ccID] = append(vals[ccID], pt)
|
|
}
|
|
sort.Slice(vals[ccID], func(i, j int) bool { return vals[ccID][i].X < vals[ccID][j].X })
|
|
}
|
|
|
|
p := plot.New()
|
|
p.Title.Text = "Company Money"
|
|
p.X.Label.Text = "Year"
|
|
if unitName != "" {
|
|
p.Y.Label.Text = fmt.Sprintf("Money (%s)", unitName)
|
|
} else {
|
|
p.Y.Label.Text = "Money"
|
|
}
|
|
|
|
i := 0
|
|
for ccID, xys := range vals {
|
|
cc := cfg.Clients[ccID]
|
|
|
|
l, s, err := plotter.NewLinePoints(xys)
|
|
logErrorDebug(err, "botGraphMoney : plotter.NewLinePoints")
|
|
if err != nil {
|
|
bot.SendChat(m.Chat.ID, fmt.Sprintf("plotter.NewLinePoints : %s", err))
|
|
return
|
|
}
|
|
|
|
l.Color = plotutil.Color(i)
|
|
l.Dashes = plotutil.Dashes(2)
|
|
s.Color = plotutil.Color(i)
|
|
s.Shape = plotutil.Shape(0)
|
|
|
|
p.Add(l)
|
|
p.Add(s)
|
|
p.Legend.Add(cc.Username, l, s)
|
|
|
|
i++
|
|
}
|
|
|
|
err := p.Save(12*vg.Inch, 8*vg.Inch, "/app/data/points.png")
|
|
logErrorDebug(err, "botGraphMoney : plot.Save")
|
|
if err != nil {
|
|
bot.SendChat(m.Chat.ID, fmt.Sprintf("plot.Save : %s", err))
|
|
return
|
|
}
|
|
|
|
bot.SendChatImage(m.Chat.ID, "/app/data/points.png")
|
|
|
|
return
|
|
}
|
|
|
|
func botGraphIncome(m *tb.Message) {
|
|
var (
|
|
maxVal float64
|
|
unitFactor float64
|
|
unitName string
|
|
)
|
|
for _, dStats := range cfg.StatsMonthly {
|
|
for _, stat := range dStats {
|
|
valueFloat := float64(stat.Income)
|
|
if math.Abs(valueFloat) > maxVal {
|
|
maxVal = math.Abs(valueFloat)
|
|
}
|
|
}
|
|
}
|
|
if maxVal > 1000000000 {
|
|
unitFactor = 1000000000
|
|
unitName = "billion"
|
|
} else if maxVal > 1000000 {
|
|
unitFactor = 1000000
|
|
unitName = "million"
|
|
} else {
|
|
unitFactor = 1
|
|
unitName = ""
|
|
}
|
|
|
|
var vals map[int]plotter.XYs
|
|
vals = make(map[int]plotter.XYs)
|
|
for ccID, dStats := range cfg.StatsMonthly {
|
|
vals[ccID] = make(plotter.XYs, 0)
|
|
for dStr, stat := range dStats {
|
|
d, err := time.Parse("20060102", dStr)
|
|
logErrorDebug(err, "botGraphIncome : time.Parse")
|
|
if err != nil {
|
|
bot.SendChat(m.Chat.ID, fmt.Sprintf("time.Parse : %s", err))
|
|
return
|
|
}
|
|
dateFloat := float64(d.Year()) + float64(d.Month()-1)/12
|
|
valueFloat := float64(stat.Income)
|
|
pt := plotter.XY{
|
|
X: dateFloat,
|
|
Y: valueFloat / unitFactor,
|
|
}
|
|
vals[ccID] = append(vals[ccID], pt)
|
|
}
|
|
sort.Slice(vals[ccID], func(i, j int) bool { return vals[ccID][i].X < vals[ccID][j].X })
|
|
}
|
|
|
|
p := plot.New()
|
|
p.Title.Text = "Company Income"
|
|
p.X.Label.Text = "Year"
|
|
if unitName != "" {
|
|
p.Y.Label.Text = fmt.Sprintf("Income (%s)", unitName)
|
|
} else {
|
|
p.Y.Label.Text = "Income"
|
|
}
|
|
|
|
i := 0
|
|
for ccID, xys := range vals {
|
|
cc := cfg.Clients[ccID]
|
|
|
|
l, s, err := plotter.NewLinePoints(xys)
|
|
logErrorDebug(err, "botGraphIncome : plotter.NewLinePoints")
|
|
if err != nil {
|
|
bot.SendChat(m.Chat.ID, fmt.Sprintf("plotter.NewLinePoints : %s", err))
|
|
return
|
|
}
|
|
|
|
l.Color = plotutil.Color(i)
|
|
l.Dashes = plotutil.Dashes(2)
|
|
s.Color = plotutil.Color(i)
|
|
s.Shape = plotutil.Shape(0)
|
|
|
|
p.Add(l)
|
|
p.Add(s)
|
|
p.Legend.Add(cc.Username, l, s)
|
|
|
|
i++
|
|
}
|
|
|
|
err := p.Save(12*vg.Inch, 8*vg.Inch, "/app/data/points.png")
|
|
logErrorDebug(err, "botGraphIncome : plot.Save")
|
|
if err != nil {
|
|
bot.SendChat(m.Chat.ID, fmt.Sprintf("plot.Save : %s", err))
|
|
return
|
|
}
|
|
|
|
bot.SendChatImage(m.Chat.ID, "/app/data/points.png")
|
|
|
|
return
|
|
}
|
|
|
|
func botGraphLoan(m *tb.Message) {
|
|
var (
|
|
maxVal float64
|
|
unitFactor float64
|
|
unitName string
|
|
)
|
|
for _, dStats := range cfg.StatsMonthly {
|
|
for _, stat := range dStats {
|
|
valueFloat := float64(stat.Loan)
|
|
if math.Abs(valueFloat) > maxVal {
|
|
maxVal = math.Abs(valueFloat)
|
|
}
|
|
}
|
|
}
|
|
if maxVal > 1000000000 {
|
|
unitFactor = 1000000000
|
|
unitName = "billion"
|
|
} else if maxVal > 1000000 {
|
|
unitFactor = 1000000
|
|
unitName = "million"
|
|
} else {
|
|
unitFactor = 1
|
|
unitName = ""
|
|
}
|
|
|
|
var vals map[int]plotter.XYs
|
|
vals = make(map[int]plotter.XYs)
|
|
for ccID, dStats := range cfg.StatsMonthly {
|
|
vals[ccID] = make(plotter.XYs, 0)
|
|
for dStr, stat := range dStats {
|
|
d, err := time.Parse("20060102", dStr)
|
|
logErrorDebug(err, "botGraphLoan : time.Parse")
|
|
if err != nil {
|
|
bot.SendChat(m.Chat.ID, fmt.Sprintf("time.Parse : %s", err))
|
|
return
|
|
}
|
|
dateFloat := float64(d.Year()) + float64(d.Month()-1)/12
|
|
valueFloat := float64(stat.Loan)
|
|
pt := plotter.XY{
|
|
X: dateFloat,
|
|
Y: valueFloat / unitFactor,
|
|
}
|
|
vals[ccID] = append(vals[ccID], pt)
|
|
}
|
|
sort.Slice(vals[ccID], func(i, j int) bool { return vals[ccID][i].X < vals[ccID][j].X })
|
|
}
|
|
|
|
p := plot.New()
|
|
p.Title.Text = "Company Loan"
|
|
p.X.Label.Text = "Year"
|
|
if unitName != "" {
|
|
p.Y.Label.Text = fmt.Sprintf("Loan (%s)", unitName)
|
|
} else {
|
|
p.Y.Label.Text = "Loan"
|
|
}
|
|
|
|
i := 0
|
|
for ccID, xys := range vals {
|
|
cc := cfg.Clients[ccID]
|
|
|
|
l, s, err := plotter.NewLinePoints(xys)
|
|
logErrorDebug(err, "botGraphLoan : plotter.NewLinePoints")
|
|
if err != nil {
|
|
bot.SendChat(m.Chat.ID, fmt.Sprintf("plotter.NewLinePoints : %s", err))
|
|
return
|
|
}
|
|
|
|
l.Color = plotutil.Color(i)
|
|
l.Dashes = plotutil.Dashes(2)
|
|
s.Color = plotutil.Color(i)
|
|
s.Shape = plotutil.Shape(0)
|
|
|
|
p.Add(l)
|
|
p.Add(s)
|
|
p.Legend.Add(cc.Username, l, s)
|
|
|
|
i++
|
|
}
|
|
|
|
err := p.Save(12*vg.Inch, 8*vg.Inch, "/app/data/points.png")
|
|
logErrorDebug(err, "botGraphLoan : plot.Save")
|
|
if err != nil {
|
|
bot.SendChat(m.Chat.ID, fmt.Sprintf("plot.Save : %s", err))
|
|
return
|
|
}
|
|
|
|
bot.SendChatImage(m.Chat.ID, "/app/data/points.png")
|
|
|
|
return
|
|
}
|
|
|
|
func botGraphPerf(m *tb.Message) {
|
|
var (
|
|
maxVal float64
|
|
unitFactor float64
|
|
unitName string
|
|
)
|
|
for _, dStats := range cfg.StatsMonthly {
|
|
for _, stat := range dStats {
|
|
valueFloat := float64(stat.PerformancePreviousQuarter)
|
|
if math.Abs(valueFloat) > maxVal {
|
|
maxVal = math.Abs(valueFloat)
|
|
}
|
|
}
|
|
}
|
|
if maxVal > 1000000000 {
|
|
unitFactor = 1000000000
|
|
unitName = "billion"
|
|
} else if maxVal > 1000000 {
|
|
unitFactor = 1000000
|
|
unitName = "million"
|
|
} else {
|
|
unitFactor = 1
|
|
unitName = ""
|
|
}
|
|
|
|
var vals map[int]plotter.XYs
|
|
vals = make(map[int]plotter.XYs)
|
|
for ccID, dStats := range cfg.StatsMonthly {
|
|
vals[ccID] = make(plotter.XYs, 0)
|
|
for dStr, stat := range dStats {
|
|
d, err := time.Parse("20060102", dStr)
|
|
logErrorDebug(err, "botGraphPerf : time.Parse")
|
|
if err != nil {
|
|
bot.SendChat(m.Chat.ID, fmt.Sprintf("time.Parse : %s", err))
|
|
return
|
|
}
|
|
dateFloat := float64(d.Year()) + float64(d.Month()-1)/12
|
|
valueFloat := float64(stat.PerformancePreviousQuarter)
|
|
pt := plotter.XY{
|
|
X: dateFloat,
|
|
Y: valueFloat / unitFactor,
|
|
}
|
|
vals[ccID] = append(vals[ccID], pt)
|
|
}
|
|
sort.Slice(vals[ccID], func(i, j int) bool { return vals[ccID][i].X < vals[ccID][j].X })
|
|
}
|
|
|
|
p := plot.New()
|
|
p.Title.Text = "Company Perf"
|
|
p.X.Label.Text = "Year"
|
|
if unitName != "" {
|
|
p.Y.Label.Text = fmt.Sprintf("Perf (%s)", unitName)
|
|
} else {
|
|
p.Y.Label.Text = "Perf"
|
|
}
|
|
|
|
i := 0
|
|
for ccID, xys := range vals {
|
|
cc := cfg.Clients[ccID]
|
|
|
|
l, s, err := plotter.NewLinePoints(xys)
|
|
logErrorDebug(err, "botGraphPerf : plotter.NewLinePoints")
|
|
if err != nil {
|
|
bot.SendChat(m.Chat.ID, fmt.Sprintf("plotter.NewLinePoints : %s", err))
|
|
return
|
|
}
|
|
|
|
l.Color = plotutil.Color(i)
|
|
l.Dashes = plotutil.Dashes(2)
|
|
s.Color = plotutil.Color(i)
|
|
s.Shape = plotutil.Shape(0)
|
|
|
|
p.Add(l)
|
|
p.Add(s)
|
|
p.Legend.Add(cc.Username, l, s)
|
|
|
|
i++
|
|
}
|
|
|
|
err := p.Save(12*vg.Inch, 8*vg.Inch, "/app/data/points.png")
|
|
logErrorDebug(err, "botGraphPerf : plot.Save")
|
|
if err != nil {
|
|
bot.SendChat(m.Chat.ID, fmt.Sprintf("plot.Save : %s", err))
|
|
return
|
|
}
|
|
|
|
bot.SendChatImage(m.Chat.ID, "/app/data/points.png")
|
|
|
|
return
|
|
}
|
|
|
|
func botGraphPlanes(m *tb.Message) {
|
|
var planes, airports map[int]plotter.XYs
|
|
planes = make(map[int]plotter.XYs)
|
|
airports = make(map[int]plotter.XYs)
|
|
for ccID, dStats := range cfg.StatsMonthly {
|
|
planes[ccID] = make(plotter.XYs, 0)
|
|
for dStr, stat := range dStats {
|
|
d, err := time.Parse("20060102", dStr)
|
|
logErrorDebug(err, "botGraphPlanes : time.Parse")
|
|
if err != nil {
|
|
bot.SendChat(m.Chat.ID, fmt.Sprintf("time.Parse : %s", err))
|
|
return
|
|
}
|
|
dateFloat := float64(d.Year()) + float64(d.Month()-1)/12
|
|
pt := plotter.XY{
|
|
X: dateFloat,
|
|
Y: float64(stat.Planes),
|
|
}
|
|
planes[ccID] = append(planes[ccID], pt)
|
|
pt = plotter.XY{
|
|
X: dateFloat,
|
|
Y: float64(stat.Airports),
|
|
}
|
|
airports[ccID] = append(airports[ccID], pt)
|
|
}
|
|
sort.Slice(planes[ccID], func(i, j int) bool { return planes[ccID][i].X < planes[ccID][j].X })
|
|
sort.Slice(airports[ccID], func(i, j int) bool { return airports[ccID][i].X < airports[ccID][j].X })
|
|
}
|
|
|
|
p := plot.New()
|
|
p.Title.Text = "Planes summary"
|
|
p.X.Label.Text = "Year"
|
|
p.Y.Label.Text = "Planes"
|
|
p.Y.Min = 0
|
|
|
|
i := 0
|
|
for ccID, xys := range planes {
|
|
cc := cfg.Clients[ccID]
|
|
|
|
l, s, err := plotter.NewLinePoints(xys)
|
|
logErrorDebug(err, "botGraphPlanes : plotter.NewLinePoints")
|
|
if err != nil {
|
|
bot.SendChat(m.Chat.ID, fmt.Sprintf("plotter.NewLinePoints : %s", err))
|
|
return
|
|
}
|
|
|
|
l.Color = plotutil.Color(i)
|
|
l.Dashes = plotutil.Dashes(2)
|
|
s.Color = plotutil.Color(i)
|
|
s.Shape = plotutil.Shape(0)
|
|
|
|
p.Add(l)
|
|
p.Add(s)
|
|
p.Legend.Add(cc.Username, l, s)
|
|
|
|
i++
|
|
}
|
|
|
|
err := p.Save(12*vg.Inch, 8*vg.Inch, "/app/data/points.png")
|
|
logErrorDebug(err, "botGraphPlanes : plot.Save")
|
|
if err != nil {
|
|
bot.SendChat(m.Chat.ID, fmt.Sprintf("plot.Save : %s", err))
|
|
return
|
|
}
|
|
|
|
bot.SendChatImage(m.Chat.ID, "/app/data/points.png")
|
|
|
|
return
|
|
}
|
|
|
|
func botGraphBusses(m *tb.Message) {
|
|
var busses, busStops map[int]plotter.XYs
|
|
busses = make(map[int]plotter.XYs)
|
|
busStops = make(map[int]plotter.XYs)
|
|
for ccID, dStats := range cfg.StatsMonthly {
|
|
busses[ccID] = make(plotter.XYs, 0)
|
|
for dStr, stat := range dStats {
|
|
d, err := time.Parse("20060102", dStr)
|
|
logErrorDebug(err, "botGraphBusses : time.Parse")
|
|
if err != nil {
|
|
bot.SendChat(m.Chat.ID, fmt.Sprintf("time.Parse : %s", err))
|
|
return
|
|
}
|
|
dateFloat := float64(d.Year()) + float64(d.Month()-1)/12
|
|
pt := plotter.XY{
|
|
X: dateFloat,
|
|
Y: float64(stat.Busses),
|
|
}
|
|
busses[ccID] = append(busses[ccID], pt)
|
|
pt = plotter.XY{
|
|
X: dateFloat,
|
|
Y: float64(stat.BusStops),
|
|
}
|
|
busStops[ccID] = append(busStops[ccID], pt)
|
|
}
|
|
sort.Slice(busses[ccID], func(i, j int) bool { return busses[ccID][i].X < busses[ccID][j].X })
|
|
sort.Slice(busStops[ccID], func(i, j int) bool { return busStops[ccID][i].X < busStops[ccID][j].X })
|
|
}
|
|
|
|
p := plot.New()
|
|
p.Title.Text = "Busses summary"
|
|
p.X.Label.Text = "Year"
|
|
p.Y.Label.Text = "Busses"
|
|
p.Y.Min = 0
|
|
|
|
i := 0
|
|
for ccID, xys := range busses {
|
|
cc := cfg.Clients[ccID]
|
|
|
|
l, s, err := plotter.NewLinePoints(xys)
|
|
logErrorDebug(err, "botGraphBusses : plotter.NewLinePoints")
|
|
if err != nil {
|
|
bot.SendChat(m.Chat.ID, fmt.Sprintf("plotter.NewLinePoints : %s", err))
|
|
return
|
|
}
|
|
|
|
l.Color = plotutil.Color(i)
|
|
l.Dashes = plotutil.Dashes(2)
|
|
s.Color = plotutil.Color(i)
|
|
s.Shape = plotutil.Shape(0)
|
|
|
|
p.Add(l)
|
|
p.Add(s)
|
|
p.Legend.Add(cc.Username, l, s)
|
|
|
|
i++
|
|
}
|
|
|
|
err := p.Save(12*vg.Inch, 8*vg.Inch, "/app/data/points.png")
|
|
logErrorDebug(err, "botGraphBusses : plot.Save")
|
|
if err != nil {
|
|
bot.SendChat(m.Chat.ID, fmt.Sprintf("plot.Save : %s", err))
|
|
return
|
|
}
|
|
|
|
bot.SendChatImage(m.Chat.ID, "/app/data/points.png")
|
|
|
|
return
|
|
}
|
|
|
|
func botGraphTrains(m *tb.Message) {
|
|
var trains, trainStations map[int]plotter.XYs
|
|
trains = make(map[int]plotter.XYs)
|
|
trainStations = make(map[int]plotter.XYs)
|
|
for ccID, dStats := range cfg.StatsMonthly {
|
|
trains[ccID] = make(plotter.XYs, 0)
|
|
for dStr, stat := range dStats {
|
|
d, err := time.Parse("20060102", dStr)
|
|
logErrorDebug(err, "botGraphTrains : time.Parse")
|
|
if err != nil {
|
|
bot.SendChat(m.Chat.ID, fmt.Sprintf("time.Parse : %s", err))
|
|
return
|
|
}
|
|
dateFloat := float64(d.Year()) + float64(d.Month()-1)/12
|
|
pt := plotter.XY{
|
|
X: dateFloat,
|
|
Y: float64(stat.Trains),
|
|
}
|
|
trains[ccID] = append(trains[ccID], pt)
|
|
pt = plotter.XY{
|
|
X: dateFloat,
|
|
Y: float64(stat.TrainStations),
|
|
}
|
|
trainStations[ccID] = append(trainStations[ccID], pt)
|
|
}
|
|
sort.Slice(trains[ccID], func(i, j int) bool { return trains[ccID][i].X < trains[ccID][j].X })
|
|
sort.Slice(trainStations[ccID], func(i, j int) bool { return trainStations[ccID][i].X < trainStations[ccID][j].X })
|
|
}
|
|
|
|
p := plot.New()
|
|
p.Title.Text = "Trains summary"
|
|
p.X.Label.Text = "Year"
|
|
p.Y.Label.Text = "Trains"
|
|
p.Y.Min = 0
|
|
|
|
i := 0
|
|
for ccID, xys := range trains {
|
|
cc := cfg.Clients[ccID]
|
|
|
|
l, s, err := plotter.NewLinePoints(xys)
|
|
logErrorDebug(err, "botGraphTrains : plotter.NewLinePoints")
|
|
if err != nil {
|
|
bot.SendChat(m.Chat.ID, fmt.Sprintf("plotter.NewLinePoints : %s", err))
|
|
return
|
|
}
|
|
|
|
l.Color = plotutil.Color(i)
|
|
l.Dashes = plotutil.Dashes(2)
|
|
s.Color = plotutil.Color(i)
|
|
s.Shape = plotutil.Shape(0)
|
|
|
|
p.Add(l)
|
|
p.Add(s)
|
|
p.Legend.Add(cc.Username, l, s)
|
|
|
|
i++
|
|
}
|
|
|
|
err := p.Save(12*vg.Inch, 8*vg.Inch, "/app/data/points.png")
|
|
logErrorDebug(err, "botGraphTrains : plot.Save")
|
|
if err != nil {
|
|
bot.SendChat(m.Chat.ID, fmt.Sprintf("plot.Save : %s", err))
|
|
return
|
|
}
|
|
|
|
bot.SendChatImage(m.Chat.ID, "/app/data/points.png")
|
|
|
|
return
|
|
}
|
|
|
|
func botGraphLorries(m *tb.Message) {
|
|
var lorries, lorriesStations map[int]plotter.XYs
|
|
lorries = make(map[int]plotter.XYs)
|
|
lorriesStations = make(map[int]plotter.XYs)
|
|
for ccID, dStats := range cfg.StatsMonthly {
|
|
lorries[ccID] = make(plotter.XYs, 0)
|
|
for dStr, stat := range dStats {
|
|
d, err := time.Parse("20060102", dStr)
|
|
logErrorDebug(err, "botGraphLorries : time.Parse")
|
|
if err != nil {
|
|
bot.SendChat(m.Chat.ID, fmt.Sprintf("time.Parse : %s", err))
|
|
return
|
|
}
|
|
dateFloat := float64(d.Year()) + float64(d.Month()-1)/12
|
|
pt := plotter.XY{
|
|
X: dateFloat,
|
|
Y: float64(stat.Lorries),
|
|
}
|
|
lorries[ccID] = append(lorries[ccID], pt)
|
|
pt = plotter.XY{
|
|
X: dateFloat,
|
|
Y: float64(stat.LorryStations),
|
|
}
|
|
lorriesStations[ccID] = append(lorriesStations[ccID], pt)
|
|
}
|
|
sort.Slice(lorries[ccID], func(i, j int) bool { return lorries[ccID][i].X < lorries[ccID][j].X })
|
|
sort.Slice(lorriesStations[ccID], func(i, j int) bool { return lorriesStations[ccID][i].X < lorriesStations[ccID][j].X })
|
|
}
|
|
|
|
p := plot.New()
|
|
p.Title.Text = "Lorries summary"
|
|
p.X.Label.Text = "Year"
|
|
p.Y.Label.Text = "Lorries"
|
|
p.Y.Min = 0
|
|
|
|
i := 0
|
|
for ccID, xys := range lorries {
|
|
cc := cfg.Clients[ccID]
|
|
|
|
l, s, err := plotter.NewLinePoints(xys)
|
|
logErrorDebug(err, "botGraphLorries : plotter.NewLinePoints")
|
|
if err != nil {
|
|
bot.SendChat(m.Chat.ID, fmt.Sprintf("plotter.NewLinePoints : %s", err))
|
|
return
|
|
}
|
|
|
|
l.Color = plotutil.Color(i)
|
|
l.Dashes = plotutil.Dashes(2)
|
|
s.Color = plotutil.Color(i)
|
|
s.Shape = plotutil.Shape(0)
|
|
|
|
p.Add(l)
|
|
p.Add(s)
|
|
p.Legend.Add(cc.Username, l, s)
|
|
|
|
i++
|
|
}
|
|
|
|
err := p.Save(12*vg.Inch, 8*vg.Inch, "/app/data/points.png")
|
|
logErrorDebug(err, "botGraphLorries : plot.Save")
|
|
if err != nil {
|
|
bot.SendChat(m.Chat.ID, fmt.Sprintf("plot.Save : %s", err))
|
|
return
|
|
}
|
|
|
|
bot.SendChatImage(m.Chat.ID, "/app/data/points.png")
|
|
|
|
return
|
|
}
|
|
|
|
func botGraphShips(m *tb.Message) {
|
|
var ships, harbours map[int]plotter.XYs
|
|
ships = make(map[int]plotter.XYs)
|
|
harbours = make(map[int]plotter.XYs)
|
|
for ccID, dStats := range cfg.StatsMonthly {
|
|
ships[ccID] = make(plotter.XYs, 0)
|
|
for dStr, stat := range dStats {
|
|
d, err := time.Parse("20060102", dStr)
|
|
logErrorDebug(err, "botGraphShips : time.Parse")
|
|
if err != nil {
|
|
bot.SendChat(m.Chat.ID, fmt.Sprintf("time.Parse : %s", err))
|
|
return
|
|
}
|
|
dateFloat := float64(d.Year()) + float64(d.Month()-1)/12
|
|
pt := plotter.XY{
|
|
X: dateFloat,
|
|
Y: float64(stat.Ships),
|
|
}
|
|
ships[ccID] = append(ships[ccID], pt)
|
|
pt = plotter.XY{
|
|
X: dateFloat,
|
|
Y: float64(stat.Harbours),
|
|
}
|
|
harbours[ccID] = append(harbours[ccID], pt)
|
|
}
|
|
sort.Slice(ships[ccID], func(i, j int) bool { return ships[ccID][i].X < ships[ccID][j].X })
|
|
sort.Slice(harbours[ccID], func(i, j int) bool { return harbours[ccID][i].X < harbours[ccID][j].X })
|
|
}
|
|
|
|
p := plot.New()
|
|
p.Title.Text = "Ships summary"
|
|
p.X.Label.Text = "Year"
|
|
p.Y.Label.Text = "Ships"
|
|
p.Y.Min = 0
|
|
|
|
i := 0
|
|
for ccID, xys := range ships {
|
|
cc := cfg.Clients[ccID]
|
|
|
|
l, s, err := plotter.NewLinePoints(xys)
|
|
logErrorDebug(err, "botGraphShips : plotter.NewLinePoints")
|
|
if err != nil {
|
|
bot.SendChat(m.Chat.ID, fmt.Sprintf("plotter.NewLinePoints : %s", err))
|
|
return
|
|
}
|
|
|
|
l.Color = plotutil.Color(i)
|
|
l.Dashes = plotutil.Dashes(2)
|
|
s.Color = plotutil.Color(i)
|
|
s.Shape = plotutil.Shape(0)
|
|
|
|
p.Add(l)
|
|
p.Add(s)
|
|
p.Legend.Add(cc.Username, l, s)
|
|
|
|
i++
|
|
}
|
|
|
|
err := p.Save(12*vg.Inch, 8*vg.Inch, "/app/data/points.png")
|
|
logErrorDebug(err, "botGraphShips : plot.Save")
|
|
if err != nil {
|
|
bot.SendChat(m.Chat.ID, fmt.Sprintf("plot.Save : %s", err))
|
|
return
|
|
}
|
|
|
|
bot.SendChatImage(m.Chat.ID, "/app/data/points.png")
|
|
|
|
return
|
|
}
|
|
|
|
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)
|
|
logInfoDebug("botChannelPost : %s\n", string(b))
|
|
// channel posts only
|
|
}
|
|
|
|
func botQuery(q *tb.Query) {
|
|
logInfoDebug("botQuery")
|
|
// incoming inline queries
|
|
}
|
|
|
|
func botText(m *tb.Message) {
|
|
|
|
r := regexp.MustCompile("^\\/delete_(?P<Ref>[a-f0-9]{16})$")
|
|
if r.MatchString(m.Text) {
|
|
botActuallyDelete(m)
|
|
return
|
|
}
|
|
|
|
r = regexp.MustCompile("^\\/reset_(?P<Ref>[a-f0-9]{16})$")
|
|
if r.MatchString(m.Text) {
|
|
botActuallyReset(m)
|
|
return
|
|
}
|
|
|
|
r = regexp.MustCompile("^\\/start_(?P<Ref>[a-f0-9]{16})$")
|
|
if r.MatchString(m.Text) {
|
|
botActuallyStart(m)
|
|
return
|
|
}
|
|
|
|
PrintText(m)
|
|
}
|