@@ 1,213 @@
+package main
+
+import (
+ "encoding/binary"
+ "fmt"
+ "html"
+ "io/ioutil"
+ "log"
+ "strings"
+
+ "git.sr.ht/~mendelmaleh/mcstatus"
+
+ "github.com/bobesa/go-domain-util/domainutil"
+ "github.com/go-telegram-bot-api/telegram-bot-api"
+ "github.com/pelletier/go-toml"
+ "github.com/prologic/bitcask"
+)
+
+type Config struct {
+ Bot struct {
+ Token string
+ DB string
+ }
+}
+
+func mcStatusString(q string, s mcstatus.Status) string {
+ var b strings.Builder
+
+ // query, hostname, isonline
+ b.WriteString(fmt.Sprintf("<a href=\"%s\">%s</a> is %s\n", s.Hostname, html.EscapeString(q), mcStatusOnlineString(s)))
+
+ if !s.IsOnline {
+ return b.String()
+ }
+
+ b.WriteString(fmt.Sprintf("- players: %d/%d\n", s.PlayersOnline, s.PlayersMax)) // players
+ b.WriteString(fmt.Sprintf("- version: %s (%s)\n", s.Version, s.Software)) // version, software
+ b.WriteString(fmt.Sprintf("- ip: <code>%s:%d</code>\n", s.IP, s.Port)) // ip, port
+
+ return b.String()
+}
+
+func mcStatusOnlineString(s mcstatus.Status) string {
+ if s.IsOnline {
+ return "online"
+ }
+
+ return "not online"
+}
+
+func int64toBytes(i int64) []byte {
+ b := make([]byte, 8)
+ binary.LittleEndian.PutUint64(b, uint64(i))
+
+ return b
+}
+
+func main() {
+ // get and parse config
+ doc, err := ioutil.ReadFile("config.toml")
+ if err != nil {
+ log.Panic(err)
+ }
+
+ var config Config
+ if err := toml.Unmarshal(doc, &config); err != nil {
+ log.Panic(err)
+ }
+
+ // setup db
+ db, err := bitcask.Open(config.Bot.DB)
+ if err != nil {
+ log.Panic(err)
+ }
+ defer db.Close()
+
+ // setup mc
+ mc := mcstatus.New()
+
+ // setup bot
+ bot, err := tgbotapi.NewBotAPI(config.Bot.Token)
+ if err != nil {
+ log.Panic(err)
+ }
+
+ log.Printf("Authorized on account %s", bot.Self.UserName)
+ // bot.Debug = true
+
+ updates, err := bot.GetUpdatesChan(tgbotapi.UpdateConfig{Timeout: 60})
+ if err != nil {
+ log.Panic(err)
+ }
+
+ for u := range updates {
+ if q := u.InlineQuery; q != nil {
+ if dmn := domainutil.Domain(q.Query); dmn == "" {
+ continue
+ }
+
+ status, err := mc.GetStatus(q.Query)
+ if err != nil {
+ log.Println(err)
+ continue
+ }
+
+ r := tgbotapi.InlineQueryResultArticle{
+ Type: "article", // must be
+ ID: q.ID,
+ Title: q.Query,
+ Description: fmt.Sprintf("%s is %s", q.Query, mcStatusOnlineString(status)),
+ ThumbURL: mcstatus.IconURL(q.Query),
+ InputMessageContent: tgbotapi.InputTextMessageContent{
+ DisableWebPagePreview: true,
+ ParseMode: "html",
+ Text: mcStatusString(q.Query, status),
+ },
+ }
+
+ results := make([]interface{}, 1)
+ results[0] = r
+
+ api, err := bot.AnswerInlineQuery(tgbotapi.InlineConfig{
+ InlineQueryID: q.ID,
+ Results: results,
+ })
+
+ if err != nil {
+ log.Println(api, err)
+ }
+
+ continue
+ }
+
+ if m := u.Message; m != nil {
+ if m.Command() == "status" {
+ chatid := int64toBytes(m.Chat.ID)
+ q := m.CommandArguments()
+ if q == "" && !db.Has(chatid) {
+ continue // TODO
+ }
+
+ if q == "" {
+ val, err := db.Get(chatid)
+ if err != nil {
+ log.Println(err)
+ continue // TODO
+ }
+
+ q = string(val)
+ } else {
+ q = strings.Fields(q)[0]
+
+ if m.Chat.IsPrivate() {
+ err := db.Put(chatid, []byte(q))
+ if err != nil {
+ log.Println(err)
+ continue // TODO
+ }
+ }
+ }
+
+ status, err := mc.GetStatus(q)
+ if err != nil {
+ log.Println(err)
+ continue // TODO
+ }
+
+ msg := tgbotapi.NewMessage(m.Chat.ID, mcStatusString(q, status))
+ msg.ParseMode = "html"
+ msg.DisableWebPagePreview = true
+
+ if api, err := bot.Send(msg); err != nil {
+ log.Println(api, err)
+ }
+ } else if m.Command() == "default" {
+ if m.Chat.IsPrivate() {
+ continue
+ }
+
+ chatmember, err := bot.GetChatMember(tgbotapi.ChatConfigWithUser{
+ ChatID: m.Chat.ID,
+ UserID: m.From.ID,
+ })
+ if err != nil {
+ log.Println(err)
+ continue
+ }
+
+ if !chatmember.IsAdministrator() && !chatmember.IsCreator() {
+ continue // TODO
+ }
+
+ args := strings.Fields(m.CommandArguments())
+ if len(args) < 1 || len(args[0]) < 1 {
+ continue // TODO
+ }
+
+ q := args[0]
+ if q == "clear" {
+ if err := db.Delete(int64toBytes(m.Chat.ID)); err != nil {
+ log.Println(err)
+ }
+
+ continue
+ }
+
+ if err := db.Put(int64toBytes(m.Chat.ID), []byte(q)); err != nil {
+ log.Println(err)
+ }
+ }
+ }
+ }
+}