184 lines
3.4 KiB
Go
184 lines
3.4 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"encoding/json"
|
|
"errors"
|
|
"io"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/Tnze/go-mc/bot"
|
|
"github.com/Tnze/go-mc/bot/basic"
|
|
"github.com/Tnze/go-mc/chat"
|
|
"github.com/Tnze/go-mc/data/packetid"
|
|
"github.com/Tnze/go-mc/net/packet"
|
|
"github.com/bwmarrin/discordgo"
|
|
"github.com/google/uuid"
|
|
"github.com/rs/zerolog/log"
|
|
)
|
|
|
|
var discordFmt = strings.NewReplacer(
|
|
"_", `\_`,
|
|
"*", `\*`,
|
|
"~", `\~`,
|
|
)
|
|
|
|
type Client struct {
|
|
discord *discordgo.Session
|
|
channelID string
|
|
|
|
minecraft *bot.Client
|
|
serverIP string
|
|
}
|
|
|
|
func NewClient(token, serverIP, discordToken string) (*Client, error) {
|
|
c := &Client{
|
|
serverIP: serverIP,
|
|
}
|
|
if err := c.setupMinecraft(token); err != nil {
|
|
return nil, err
|
|
}
|
|
if discordToken != "" {
|
|
if err := c.setupDiscord(discordToken); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return c, nil
|
|
}
|
|
|
|
func (c *Client) Run() {
|
|
go c.onStdin()
|
|
c.handleGame()
|
|
}
|
|
|
|
func (c *Client) handleGame() {
|
|
for {
|
|
err := c.minecraft.JoinServer(c.serverIP)
|
|
if err != nil {
|
|
log.Err(err).Msg("")
|
|
<-time.After(time.Minute * 5)
|
|
return
|
|
}
|
|
log.Info().Msg("Login success")
|
|
|
|
for {
|
|
err := c.minecraft.HandleGame()
|
|
if err != nil {
|
|
log.Err(err).Msg("")
|
|
if errors.Is(err, io.EOF) {
|
|
<-time.After(time.Minute * 5)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (c *Client) handleInput(input string) {
|
|
switch strings.ToLower(input) {
|
|
case ".cook":
|
|
go c.cook()
|
|
default:
|
|
c.sendChat(input)
|
|
}
|
|
}
|
|
|
|
func (c *Client) sendChat(msg string) {
|
|
if err := c.minecraft.Conn.WritePacket(packet.Marshal(
|
|
packetid.ServerboundChat,
|
|
packet.String(msg),
|
|
)); err != nil {
|
|
log.Err(err).Msg("")
|
|
}
|
|
}
|
|
|
|
func (c *Client) onChatMsg(msg chat.Message, _ byte, _ uuid.UUID) error {
|
|
if c.discord != nil {
|
|
_, err := c.discord.ChannelMessageSend(c.channelID, discordFmt.Replace(msg.ClearString()))
|
|
return err
|
|
}
|
|
log.Info().Msg(msg.String())
|
|
return nil
|
|
}
|
|
|
|
func (c *Client) setupDiscord(token string) error {
|
|
var err error
|
|
c.discord, err = discordgo.New("Bot " + token)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
info, err := c.discord.Application("@me")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ch, err := c.discord.UserChannelCreate(info.Owner.ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
c.channelID = ch.ID
|
|
|
|
c.discord.AddHandler(func(s *discordgo.Session, m *discordgo.MessageCreate) {
|
|
if m.Author.Bot || m.ChannelID != c.channelID {
|
|
return
|
|
}
|
|
c.handleInput(m.Content)
|
|
})
|
|
|
|
return c.discord.Open()
|
|
}
|
|
|
|
func (c *Client) setupMinecraft(token string) error {
|
|
c.minecraft = bot.NewClient()
|
|
basic.NewPlayer(c.minecraft, basic.DefaultSettings)
|
|
|
|
req, err := http.NewRequest(http.MethodGet, "https://api.minecraftservices.com/minecraft/profile", nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
req.Header.Set("Authorization", "Bearer "+token)
|
|
|
|
resp, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
profile := struct {
|
|
ID string `json:"id"`
|
|
Name string `json:"name"`
|
|
}{}
|
|
if err := json.NewDecoder(resp.Body).Decode(&profile); err != nil {
|
|
return err
|
|
}
|
|
|
|
c.minecraft.Auth = bot.Auth{
|
|
Name: profile.Name,
|
|
UUID: profile.ID,
|
|
AsTk: token,
|
|
}
|
|
|
|
basic.EventsListener{
|
|
GameStart: func() error {
|
|
c.sendChat("/afk")
|
|
return nil
|
|
},
|
|
ChatMsg: c.onChatMsg,
|
|
}.Attach(c.minecraft)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *Client) onStdin() {
|
|
scanner := bufio.NewScanner(os.Stdin)
|
|
for scanner.Scan() {
|
|
msg := scanner.Text()
|
|
if strings.TrimSpace(msg) == "" {
|
|
continue
|
|
}
|
|
c.handleInput(msg)
|
|
}
|
|
}
|