211 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			211 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // The GsBot package contains some useful utilites for working with the
 | |
| // steam package. It implements authentication with sentries, server lists and
 | |
| // logging messages and events.
 | |
| //
 | |
| // Every module is optional and requires an instance of the GsBot struct.
 | |
| // Should a module have a `HandlePacket` method, you must register it with the
 | |
| // steam.Client with `RegisterPacketHandler`. Any module with a `HandleEvent`
 | |
| // method must be integrated into your event loop and should be called for each
 | |
| // event you receive.
 | |
| package gsbot
 | |
| 
 | |
| import (
 | |
| 	"encoding/hex"
 | |
| 	"encoding/json"
 | |
| 	"fmt"
 | |
| 	"io/ioutil"
 | |
| 	"log"
 | |
| 	"math/rand"
 | |
| 	"net"
 | |
| 	"os"
 | |
| 	"path"
 | |
| 	"reflect"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/Philipp15b/go-steam"
 | |
| 	"github.com/Philipp15b/go-steam/netutil"
 | |
| 	"github.com/Philipp15b/go-steam/protocol"
 | |
| 	"github.com/davecgh/go-spew/spew"
 | |
| )
 | |
| 
 | |
| // Base structure holding common data among GsBot modules.
 | |
| type GsBot struct {
 | |
| 	Client *steam.Client
 | |
| 	Log    *log.Logger
 | |
| }
 | |
| 
 | |
| // Creates a new GsBot with a new steam.Client where logs are written to stdout.
 | |
| func Default() *GsBot {
 | |
| 	return &GsBot{
 | |
| 		steam.NewClient(),
 | |
| 		log.New(os.Stdout, "", 0),
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // This module handles authentication. It logs on automatically after a ConnectedEvent
 | |
| // and saves the sentry data to a file which is also used for logon if available.
 | |
| // If you're logging on for the first time Steam may require an authcode. You can then
 | |
| // connect again with the new logon details.
 | |
| type Auth struct {
 | |
| 	bot             *GsBot
 | |
| 	details         *LogOnDetails
 | |
| 	sentryPath      string
 | |
| 	machineAuthHash []byte
 | |
| }
 | |
| 
 | |
| func NewAuth(bot *GsBot, details *LogOnDetails, sentryPath string) *Auth {
 | |
| 	return &Auth{
 | |
| 		bot:        bot,
 | |
| 		details:    details,
 | |
| 		sentryPath: sentryPath,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| type LogOnDetails struct {
 | |
| 	Username      string
 | |
| 	Password      string
 | |
| 	AuthCode      string
 | |
| 	TwoFactorCode string
 | |
| }
 | |
| 
 | |
| // This is called automatically after every ConnectedEvent, but must be called once again manually
 | |
| // with an authcode if Steam requires it when logging on for the first time.
 | |
| func (a *Auth) LogOn(details *LogOnDetails) {
 | |
| 	a.details = details
 | |
| 	sentry, err := ioutil.ReadFile(a.sentryPath)
 | |
| 	if err != nil {
 | |
| 		a.bot.Log.Printf("Error loading sentry file from path %v - This is normal if you're logging in for the first time.\n", a.sentryPath)
 | |
| 	}
 | |
| 	a.bot.Client.Auth.LogOn(&steam.LogOnDetails{
 | |
| 		Username:       details.Username,
 | |
| 		Password:       details.Password,
 | |
| 		SentryFileHash: sentry,
 | |
| 		AuthCode:       details.AuthCode,
 | |
| 		TwoFactorCode:  details.TwoFactorCode,
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func (a *Auth) HandleEvent(event interface{}) {
 | |
| 	switch e := event.(type) {
 | |
| 	case *steam.ConnectedEvent:
 | |
| 		a.LogOn(a.details)
 | |
| 	case *steam.LoggedOnEvent:
 | |
| 		a.bot.Log.Printf("Logged on (%v) with SteamId %v and account flags %v", e.Result, e.ClientSteamId, e.AccountFlags)
 | |
| 	case *steam.MachineAuthUpdateEvent:
 | |
| 		a.machineAuthHash = e.Hash
 | |
| 		err := ioutil.WriteFile(a.sentryPath, e.Hash, 0666)
 | |
| 		if err != nil {
 | |
| 			panic(err)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // This module saves the server list from ClientCMListEvent and uses
 | |
| // it when you call `Connect()`.
 | |
| type ServerList struct {
 | |
| 	bot      *GsBot
 | |
| 	listPath string
 | |
| }
 | |
| 
 | |
| func NewServerList(bot *GsBot, listPath string) *ServerList {
 | |
| 	return &ServerList{
 | |
| 		bot,
 | |
| 		listPath,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (s *ServerList) HandleEvent(event interface{}) {
 | |
| 	switch e := event.(type) {
 | |
| 	case *steam.ClientCMListEvent:
 | |
| 		d, err := json.Marshal(e.Addresses)
 | |
| 		if err != nil {
 | |
| 			panic(err)
 | |
| 		}
 | |
| 		err = ioutil.WriteFile(s.listPath, d, 0666)
 | |
| 		if err != nil {
 | |
| 			panic(err)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (s *ServerList) Connect() (bool, error) {
 | |
| 	return s.ConnectBind(nil)
 | |
| }
 | |
| 
 | |
| func (s *ServerList) ConnectBind(laddr *net.TCPAddr) (bool, error) {
 | |
| 	d, err := ioutil.ReadFile(s.listPath)
 | |
| 	if err != nil {
 | |
| 		s.bot.Log.Println("Connecting to random server.")
 | |
| 		s.bot.Client.Connect()
 | |
| 		return false, nil
 | |
| 	}
 | |
| 	var addrs []*netutil.PortAddr
 | |
| 	err = json.Unmarshal(d, &addrs)
 | |
| 	if err != nil {
 | |
| 		return false, err
 | |
| 	}
 | |
| 	raddr := addrs[rand.Intn(len(addrs))]
 | |
| 	s.bot.Log.Printf("Connecting to %v from server list\n", raddr)
 | |
| 	s.bot.Client.ConnectToBind(raddr, laddr)
 | |
| 	return true, nil
 | |
| }
 | |
| 
 | |
| // This module logs incoming packets and events to a directory.
 | |
| type Debug struct {
 | |
| 	packetId, eventId uint64
 | |
| 	bot               *GsBot
 | |
| 	base              string
 | |
| }
 | |
| 
 | |
| func NewDebug(bot *GsBot, base string) (*Debug, error) {
 | |
| 	base = path.Join(base, fmt.Sprint(time.Now().Unix()))
 | |
| 	err := os.MkdirAll(path.Join(base, "events"), 0700)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	err = os.MkdirAll(path.Join(base, "packets"), 0700)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	return &Debug{
 | |
| 		0, 0,
 | |
| 		bot,
 | |
| 		base,
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| func (d *Debug) HandlePacket(packet *protocol.Packet) {
 | |
| 	d.packetId++
 | |
| 	name := path.Join(d.base, "packets", fmt.Sprintf("%d_%d_%s", time.Now().Unix(), d.packetId, packet.EMsg))
 | |
| 
 | |
| 	text := packet.String() + "\n\n" + hex.Dump(packet.Data)
 | |
| 	err := ioutil.WriteFile(name+".txt", []byte(text), 0666)
 | |
| 	if err != nil {
 | |
| 		panic(err)
 | |
| 	}
 | |
| 
 | |
| 	err = ioutil.WriteFile(name+".bin", packet.Data, 0666)
 | |
| 	if err != nil {
 | |
| 		panic(err)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (d *Debug) HandleEvent(event interface{}) {
 | |
| 	d.eventId++
 | |
| 	name := fmt.Sprintf("%d_%d_%s.txt", time.Now().Unix(), d.eventId, name(event))
 | |
| 	err := ioutil.WriteFile(path.Join(d.base, "events", name), []byte(spew.Sdump(event)), 0666)
 | |
| 	if err != nil {
 | |
| 		panic(err)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func name(obj interface{}) string {
 | |
| 	val := reflect.ValueOf(obj)
 | |
| 	ind := reflect.Indirect(val)
 | |
| 	if ind.IsValid() {
 | |
| 		return ind.Type().Name()
 | |
| 	} else {
 | |
| 		return val.Type().Name()
 | |
| 	}
 | |
| }
 | 
