package main import ( "bufio" "encoding/binary" "fmt" "net" "regexp" "strconv" "time" tb "gopkg.in/tucnak/telebot.v2" ) type Client struct { ClientID uint32 Name string Address string CompanyID uint8 } var ( bot *tb.Bot clients map[uint32]*Client paused bool = true forcePaused bool = true forcePausedBy string githash string buildstamp string commits string ) func main() { clients = make(map[uint32]*Client) conn, err := net.Dial("tcp", "poop.siteop.biz:3977") failError(err, "net.Dial") logInfoDebug("Connected to poop.siteop.biz:3977") //send auth p := PacketAdminJoin{ Packet: Packet{PType: AdminPacketAdminJoin}, Password: "plop", AppName: "gottdad", AppVersion: "alpha", } _, err = conn.Write(p.Bytes()) failError(err, "conn.Write") r := bufio.NewReader(conn) b := make([]byte, 0xFFFF) read := 0 n := 0 // Registering bot bot, err = tb.NewBot(tb.Settings{ Token: "954090437:AAEMYeUWGluKZRwXi_K3-T23ZVpFoqQAmu0", URL: "https://api.telegram.org", Poller: &tb.LongPoller{Timeout: 10 * time.Second}, }) failError(err, "Registering bot") logInfoDebug("Connected to Telegram") go BotHandlers(bot) u := tb.User{ ID: int(32173684), } bot.Send(&u, fmt.Sprintf("Started (%s-b%s - %s)", githash, commits, buildstamp)) for { p := Packet{} for { if read >= 3 { //logInfoDebug("Packet read") break } n, err = r.Read(b[read:]) logErrorDebug(err, "r.Read") read += n //logInfoDebug("Waiting for packet, read %d bytes.", read) } p.PLength = binary.LittleEndian.Uint16(b[0:]) p.PType = b[2] if p.PLength <= 3 { logInfoAlert("Wrong packet length") break } //logInfoDebug("Waiting for packet data : len : %d / type : %d", p.PLength, p.PType) for { if read >= int(p.PLength) { //logInfoDebug("Data read") break } n, err = r.Read(b[read:]) logErrorDebug(err, "r.Read") read += n //logInfoDebug("Waiting for data, read %d/%d bytes.", read, p.PLength) } switch p.PType { case AdminPacketServerProtocol: sp := PacketServerProtocol{ Packet: p, } sp.Read(b[:p.PLength]) logInfoDebug("AdminPacketServerProtocol :\n- ProtocolVersion: %v\n- FurtherData: %v\n- UpdatePacketType: %v\n- FrequenciesAllowed: %b", sp.ProtocolVersion, sp.FurtherData, sp.UpdatePacketType, sp.FrequenciesAllowed) case AdminPacketServerWelcome: sp := PacketServerWelcome{ Packet: p, } sp.Read(b[:p.PLength]) logInfoDebug("AdminPacketServerWelcome :\n- ServerName: %v\n- OpenTTDVersion: %v\n- Dedicated: %v\n- MapSeed: %x\n- MapLandscape: %v\n- MapStartDate: %v\n- Size: %v x %v", sp.ServerName, sp.OpenTTDVersion, sp.Dedicated, sp.MapSeed, sp.MapLandscape, sp.MapStartDate, sp.MapX, sp.MapY) px := PacketAdminUpdateFrequency{ Packet: Packet{PType: AdminPacketAdminUpdateFrequency}, UpdateType: AdminUpdateDate, UpdateFrequency: AdminFrequencyDaily, } _, err = conn.Write(px.Bytes()) px = PacketAdminUpdateFrequency{ Packet: Packet{PType: AdminPacketAdminUpdateFrequency}, UpdateType: AdminUpdateClientInfo, UpdateFrequency: AdminFrequencyAutomatic, } _, err = conn.Write(px.Bytes()) px = PacketAdminUpdateFrequency{ Packet: Packet{PType: AdminPacketAdminUpdateFrequency}, UpdateType: AdminUpdateCompanyInfo, UpdateFrequency: AdminFrequencyAutomatic, } _, err = conn.Write(px.Bytes()) px = PacketAdminUpdateFrequency{ Packet: Packet{PType: AdminPacketAdminUpdateFrequency}, UpdateType: AdminUpdateChat, UpdateFrequency: AdminFrequencyAutomatic, } _, err = conn.Write(px.Bytes()) px = PacketAdminUpdateFrequency{ Packet: Packet{PType: AdminPacketAdminUpdateFrequency}, UpdateType: AdminUpdateConsole, UpdateFrequency: AdminFrequencyAutomatic, } _, err = conn.Write(px.Bytes()) case AdminPacketServerDate: sp := PacketServerDate{ Packet: p, } sp.Read(b[:p.PLength]) logInfoDebug("AdminPacketServerDate :\n- Date: %d\n- RealDate : %v", sp.Date, toDate(sp.Date)) paused = false case AdminPacketServerClientJoin: sp := PacketServerClientJoin{ Packet: p, } sp.Read(b[:p.PLength]) logInfoDebug("AdminPacketServerClientJoin :\n- ClientID: %d", sp.ClientID) sendChat(-436055948, fmt.Sprintf("%s joining.", clients[sp.ClientID].Name)) case AdminPacketServerClientInfo: sp := PacketServerClientInfo{ Packet: p, } sp.Read(b[:p.PLength]) //logInfoDebug("AdminPacketServerClientInfo :\n- ClientID: %d\n- Address: %s\n- Name: %s\n- Lang: %d\n- Date: %d\n- CompanyID: %d", sp.ClientID, sp.Address, sp.Name, sp.Lang, sp.Date, sp.CompanyID) clt := Client{ ClientID: sp.ClientID, Name: sp.Name, Address: sp.Address, CompanyID: sp.CompanyID, } clients[sp.ClientID] = &clt logInfoDebug("Clients : %v", clients) case AdminPacketServerClientError: sp := PacketServerClientError{ Packet: p, } sp.Read(b[:p.PLength]) logInfoDebug("AdminPacketServerClientError :\n- ClientID: %d\n- ErrorID: %d", sp.ClientID, sp.ErrorID) case AdminPacketServerClientQuit: sp := PacketServerClientQuit{ Packet: p, } sp.Read(b[:p.PLength]) logInfoDebug("AdminPacketServerClientQuit :\n- ClientID: %d", sp.ClientID) if len(clients) == 2 && !paused { sendChat(-436055948, fmt.Sprintf("%s leaving. Game paused.", clients[sp.ClientID].Name)) } delete(clients, sp.ClientID) case AdminPacketServerChat: sp := PacketServerChat{ Packet: p, } sp.Read(b[:p.PLength]) if sp.Message == "!unpause" { logInfoDebug("AdminPacketServerChat : Unpausing") forcePaused = false forcePausedBy = clients[sp.ClientID].Name } else if sp.Message == "!pause" { logInfoDebug("AdminPacketServerChat : Pausing") forcePaused = true forcePausedBy = clients[sp.ClientID].Name } else { logInfoDebug("AdminPacketServerChat :\n- ActionID: %d\n- DestinationID: %d\n- ClientID: %d\n- Message: %s\n- Amount: %d", sp.ActionID, sp.DestinationID, sp.ClientID, sp.Message, sp.Amount) } case AdminPacketServerConsole: sp := PacketServerConsole{ Packet: p, } sp.Read(b[:p.PLength]) ok, err := regexp.MatchString("\\[udp\\] queried from .*", sp.Text) logErrorDebug(err, "queried from") if sp.Origin != "net" || ok == false { logInfoDebug("AdminPacketServerConsole :\n- Origin: %q\n- Text: %s", sp.Origin, sp.Text) } case AdminPacketServerRCon: sp := PacketServerRCon{ Packet: p, } sp.Read(b[:p.PLength]) ok, _ := regexp.MatchString("Client #[0-9]+ name: '.*' company: [0-9]+ IP: .*", sp.Output) if ok { clt := Client{} r, _ := regexp.Compile("Client #(?P[0-9]+) name: '(?P.*)' company: (?P[0-9]+) IP: (?P
.*)") ID64, _ := strconv.ParseInt(r.ReplaceAllString(sp.Output, "${ClientID}"), 10, 32) clt.ClientID = uint32(ID64) clt.Name = r.ReplaceAllString(sp.Output, "${Name}") if clt.Name == "" { clt.Name = "server" } ID64, _ = strconv.ParseInt(r.ReplaceAllString(sp.Output, "${CompanyID}"), 10, 8) clt.CompanyID = uint8(ID64) clt.Address = r.ReplaceAllString(sp.Output, "${Address}") clients[clt.ClientID] = &clt } else { logInfoDebug("AdminPacketServerRCon :\n- ColorID: %d\n- Output: %s", sp.ColorID, sp.Output) } case AdminPacketServerRConEnd: sp := PacketServerRConEnd{ Packet: p, } sp.Read(b[:p.PLength]) if sp.Command == "clients" { for k, v := range clients { logInfoDebug("Client[%d] : %s - %d (%s)", k, v.Name, v.CompanyID, v.Address) } } else { logInfoDebug("AdminPacketServerRConEnd :\n- Command: %s", sp.Command) } default: logInfoDebug("Packet fully read : len : %d / type : %d", p.PLength, p.PType) } c := make([]byte, 0xFFFF) copy(c, b[p.PLength:]) b = c read -= int(p.PLength) if len(clients) == 0 { px := PacketAdminRCon{ Packet: Packet{PType: AdminPacketAdminRCon}, Command: "clients", } _, err = conn.Write(px.Bytes()) } if !paused && forcePaused { paused = true px := PacketAdminRCon{ Packet: Packet{PType: AdminPacketAdminRCon}, Command: "pause", } _, err = conn.Write(px.Bytes()) logInfoDebug("Force pausing") if pausedBy != "" { sendChat(-436055948, fmt.Sprintf("Game paused by %s", pausedBy)) pausedBy = "" } } if paused && !forcePaused && len(clients) > 1 { // server is client #1 paused = false px := PacketAdminRCon{ Packet: Packet{PType: AdminPacketAdminRCon}, Command: "unpause", } _, err = conn.Write(px.Bytes()) logInfoDebug("Force unpausing") if pausedBy != "" { sendChat(-436055948, fmt.Sprintf("Game unpaused by %s", pausedBy)) pausedBy = "" } } if !paused && len(clients) == 1 { // server is client #1 paused = true px := PacketAdminRCon{ Packet: Packet{PType: AdminPacketAdminRCon}, Command: "pause", } _, err = conn.Write(px.Bytes()) logInfoDebug("Pausing") sendChat(-436055948, "No players. Game paused.") } } }