From 683574a8146c0c36d072c54ff479468b1b330669 Mon Sep 17 00:00:00 2001 From: Chteufleur Date: Fri, 15 Apr 2016 08:53:33 +0200 Subject: [PATCH] First working version for multi Steam and XMPP users --- database/database.go | 98 +++++++++++--------- gateway/gateway.go | 37 ++++++++ gateway/steam.go | 215 +++++++++++++++++++++++++++++++++++++++++++ gateway/xmpp.go | 154 +++++++++++++++++++++++++++++++ main.go | 183 ++---------------------------------- servers.addr | 2 +- steam/steam.go | 168 --------------------------------- xmpp/commands.go | 70 ++++++++++++-- xmpp/xmpp.go | 135 +++++++++++++-------------- xmpp4steam.cfg | 5 - 10 files changed, 602 insertions(+), 465 deletions(-) create mode 100644 gateway/gateway.go create mode 100644 gateway/steam.go create mode 100644 gateway/xmpp.go delete mode 100644 steam/steam.go diff --git a/database/database.go b/database/database.go index 5d80424..2265992 100644 --- a/database/database.go +++ b/database/database.go @@ -7,100 +7,98 @@ import ( ) const ( - databaseFile = "go_xmpp4steam.db" + databaseFile = "go_xmpp4steam.db" - createDatabaseStmt = "create table if not exists users (jid text not null primary key, steamLogin text, steamPwd text);" - insertDatabaseStmt = "insert into users (jid, steamLogin, steamPwd) values(?, ?, ?)" - deleteDatabaseStmt = "delete from users where jid = ?" - selectDatabaseStmt = "select jid, steamLogin, steamPwd from users where jid = ?" + createDatabaseStmt = "create table if not exists users (jid text not null primary key, steamLogin text, steamPwd text);" + insertDatabaseStmt = "insert into users (jid, steamLogin, steamPwd) values(?, ?, ?)" + deleteDatabaseStmt = "delete from users where jid = ?" + selectDatabaseStmt = "select jid, steamLogin, steamPwd from users where jid = ?" - LogInfo = "\t[SQLITE INFO]\t" - LogError = "\t[SQLITE ERROR]\t" - LogDebug = "\t[SQLITE DEBUG]\t" + LogInfo = "\t[SQLITE INFO]\t" + LogError = "\t[SQLITE ERROR]\t" + LogDebug = "\t[SQLITE DEBUG]\t" ) type DatabaseLine struct { - Jid string - SteamLogin string - SteamPwd string + Jid string + SteamLogin string + SteamPwd string } var ( - db = new(sql.DB) + db = new(sql.DB) ) - func init() { - d, err := sql.Open("sqlite3", databaseFile) + d, err := sql.Open("sqlite3", databaseFile) if err != nil { log.Printf("%sError on openning database", LogError, err) } - db = d + db = d - _, err = db.Exec(createDatabaseStmt) + _, err = db.Exec(createDatabaseStmt) if err != nil { - log.Printf("%sFailed to create table", LogError, err) + log.Printf("%sFailed to create table", LogError, err) } } func Close() { - db.Close() + db.Close() } - func (newLine *DatabaseLine) AddLine() bool { - log.Printf("%sAdd new line %v", LogInfo, newLine) + log.Printf("%sAdd new line %v", LogInfo, newLine) stmt, err := db.Prepare(insertDatabaseStmt) if err != nil { log.Printf("%sError on insert jid %s", LogError, newLine.Jid, err) - return false + return false } defer stmt.Close() _, err = stmt.Exec(newLine.Jid, newLine.SteamLogin, newLine.SteamPwd) if err != nil { log.Printf("%sError on creating SQL statement", LogError, err) - return false + return false } - return true + return true } func RemoveLine(jid string) bool { - // FIXME not working - log.Printf("%sRemove line %s", LogInfo, jid) + // FIXME not working + log.Printf("%sRemove line %s", LogInfo, jid) - stmt, err := db.Prepare(deleteDatabaseStmt) + stmt, err := db.Prepare(deleteDatabaseStmt) if err != nil { log.Printf("%sError on delete jid %s", LogError, jid, err) - return false + return false } defer stmt.Close() res, err := stmt.Exec(jid) if err != nil { log.Printf("%sError on delete SQL statement", LogError, err) - return false + return false } - affect, err := res.RowsAffected() + affect, err := res.RowsAffected() if err != nil { log.Printf("%sError on delete SQL statement", LogError, err) - return false + return false + } + if affect == 0 { + return false } - if affect == 0 { - return false - } - return true + return true } -func GetLine(jid string) (*DatabaseLine) { - log.Printf("%sGet line %s", LogInfo, jid) - ret := new(DatabaseLine) +func GetLine(jid string) *DatabaseLine { + log.Printf("%sGet line %s", LogInfo, jid) + ret := new(DatabaseLine) - stmt, err := db.Prepare(selectDatabaseStmt) + stmt, err := db.Prepare(selectDatabaseStmt) if err != nil { - log.Printf("%sError on select line", LogError, err) - return nil + log.Printf("%sError on select line", LogError, err) + return nil } defer stmt.Close() @@ -109,5 +107,23 @@ func GetLine(jid string) (*DatabaseLine) { log.Printf("%sError on select scan", LogError, err) } - return ret + return ret +} + +func GetAllLines() []DatabaseLine { + log.Printf("%sGet all lines", LogInfo) + var ret []DatabaseLine + + rows, err := db.Query("select jid, steamLogin, steamPwd from users") + if err != nil { + log.Printf("%sError on select query", LogError, err) + } + defer rows.Close() + for rows.Next() { + user := new(DatabaseLine) + rows.Scan(&user.Jid, &user.SteamLogin, &user.SteamPwd) + ret = append(ret, *user) + } + + return ret } diff --git a/gateway/gateway.go b/gateway/gateway.go new file mode 100644 index 0000000..44393a9 --- /dev/null +++ b/gateway/gateway.go @@ -0,0 +1,37 @@ +package gateway + +import ( + "github.com/Philipp15b/go-steam" +) + +const ( + SentryDirectory = "sentries/" +) + +var ( + VersionToSend = "" +) + +type GatewayInfo struct { + // Steam + SteamLogin string + SteamPassword string + SteamAuthCode string + SteamLoginInfo *steam.LogOnDetails + SteamClient *steam.Client + SentryFile string + FriendSteamId map[string]struct{} + SteamConnecting bool + + // XMPP + XMPP_JID_Client string + XMPP_Out chan interface{} +} + +func (g *GatewayInfo) Run() { + go g.SteamRun() +} + +func (g *GatewayInfo) SetSteamAuthCode(authCode string) { + g.SteamAuthCode = authCode +} diff --git a/gateway/steam.go b/gateway/steam.go new file mode 100644 index 0000000..08469af --- /dev/null +++ b/gateway/steam.go @@ -0,0 +1,215 @@ +package gateway + +import ( + "github.com/Philipp15b/go-steam" + "github.com/Philipp15b/go-steam/internal/steamlang" + "github.com/Philipp15b/go-steam/steamid" + + "encoding/json" + "io/ioutil" + "log" + "strconv" + // "time" +) + +const ( + serverAddrs = "servers.addr" + + State_Offline = steamlang.EPersonaState_Offline + State_Online = steamlang.EPersonaState_Online + State_Busy = steamlang.EPersonaState_Busy + State_Away = steamlang.EPersonaState_Away + State_Snooze = steamlang.EPersonaState_Snooze + State_LookingToTrade = steamlang.EPersonaState_LookingToTrade + State_LookingToPlay = steamlang.EPersonaState_LookingToPlay + State_Max = steamlang.EPersonaState_Max + + LogSteamInfo = "\t[STEAM INFO]\t" + LogSteamError = "\t[STEAM ERROR]\t" + LogSteamDebug = "\t[STEAM DEBUG]\t" +) + +func (g *GatewayInfo) SteamRun() { + log.Printf("%sRunning", LogSteamInfo) + g.setLoginInfos() + g.SteamClient = steam.NewClient() + g.SteamConnecting = false + // g.SteamClient.ConnectionTimeout = 10 * time.Second + + g.mainSteam() + + log.Printf("%sReach main method's end", LogSteamInfo) + go g.SteamRun() +} + +func (g *GatewayInfo) mainSteam() { + for event := range g.SteamClient.Events() { + switch e := event.(type) { + case *steam.ConnectedEvent: + // Connected on server + g.SteamConnecting = false + log.Printf("%sConnected on Steam serveur", LogSteamDebug) + g.SteamClient.Auth.LogOn(g.SteamLoginInfo) + + case *steam.MachineAuthUpdateEvent: + // Received sentry file + ioutil.WriteFile(g.SentryFile, e.Hash, 0666) + + case *steam.LoggedOnEvent: + // Logged on + g.SendSteamPresence(steamlang.EPersonaState_Online) + g.SendXmppMessage(XmppJidComponent, "", "Connected on Steam network") + + case steam.FatalErrorEvent: + log.Printf("%sFatalError: ", LogSteamError, e) + g.SendXmppMessage(XmppJidComponent, "", "Steam Fatal Error : "+e.Error()) + g.DisconnectAllSteamFriend() + return + + case error: + log.Printf("%s", LogSteamError, e) + g.SendXmppMessage(XmppJidComponent, "", "Steam Error : "+e.Error()) + + case *steam.ClientCMListEvent: + // Save servers addresses + b, err := json.Marshal(*e) + if err != nil { + log.Printf("%sFailed to json.Marshal() servers list", LogSteamError) + } else { + ioutil.WriteFile(serverAddrs, b, 0666) + } + + case *steam.PersonaStateEvent: + // Presenc received + steamId := e.FriendId.ToString() + name := e.Name + gameName := e.GameName + + var status string + var tpye string + switch e.State { + case State_Offline: + status = Status_offline + tpye = Type_unavailable + case State_Online: + status = Status_online + tpye = Type_available + case State_Busy: + status = Status_do_not_disturb + tpye = Type_available + case State_Away: + status = Status_away + tpye = Type_available + case State_Snooze: + status = Status_extended_away + tpye = Type_available + } + if _, ok := g.FriendSteamId[steamId]; !ok { + // Send subscribsion + g.SendXmppPresence(status, Type_subscribe, steamId+"@"+XmppJidComponent, gameName, name) + g.FriendSteamId[steamId] = struct{}{} + } + g.SendXmppPresence(status, tpye, steamId+"@"+XmppJidComponent, gameName, name) + + case *steam.ChatMsgEvent: + // Message received + g.SendXmppMessage(e.ChatterId.ToString()+"@"+XmppJidComponent, "", e.Message) + + default: + log.Printf("%s", LogSteamDebug, e) + // TODO send message + } + } +} + +func (g *GatewayInfo) setLoginInfos() { + var sentryHash steam.SentryHash + sentryHash, err := ioutil.ReadFile(g.SentryFile) + + g.SteamLoginInfo = new(steam.LogOnDetails) + g.SteamLoginInfo.Username = g.SteamLogin + g.SteamLoginInfo.Password = g.SteamPassword + + if err == nil { + g.SteamLoginInfo.SentryFileHash = sentryHash + log.Printf("%sAuthentification by SentryFileHash", LogSteamDebug) + } else if g.SteamAuthCode != "" { + g.SteamLoginInfo.AuthCode = g.SteamAuthCode + log.Printf("%sAuthentification by AuthCode (%s, %s, %s)", LogSteamDebug, g.SteamLoginInfo.Username, g.SteamLoginInfo.Password, g.SteamAuthCode) + } else { + log.Printf("%sFirst authentification (%s, %s)", LogSteamDebug, g.SteamLoginInfo.Username, g.SteamLoginInfo.Password) + } +} + +func (g *GatewayInfo) IsSteamConnected() bool { + return g.SteamClient.Connected() +} + +func (g *GatewayInfo) SteamConnect() { + if g.IsSteamConnected() { + log.Printf("%sTry to connect, but already connected", LogSteamDebug) + return + } + if g.SteamConnecting { + log.Printf("%sTry to connect, but currently connecting…", LogSteamDebug) + return + } + + g.SteamConnecting = true + b, err := ioutil.ReadFile(serverAddrs) + if err == nil { + var toList steam.ClientCMListEvent + err := json.Unmarshal(b, &toList) + if err != nil { + log.Printf("%sFailed to json.Unmarshal() servers list", LogSteamError) + } else { + log.Printf("%sConnecting...", LogSteamInfo, toList.Addresses[0]) + g.SteamClient.ConnectTo(toList.Addresses[0]) + } + } else { + log.Printf("%sFailed to read servers list file", LogSteamError) + log.Printf("%sConnecting...", LogSteamInfo) + g.SteamClient.Connect() + } +} + +func (g *GatewayInfo) SteamDisconnect() { + if !g.IsSteamConnected() { + log.Printf("%sTry to disconnect, but already disconnected", LogSteamDebug) + return + } + log.Printf("%sSteam disconnect", LogSteamInfo) + + g.XMPP_Disconnect() + g.DisconnectAllSteamFriend() + go g.SteamClient.Disconnect() +} + +func (g *GatewayInfo) DisconnectAllSteamFriend() { + for sid, _ := range g.FriendSteamId { + g.SendXmppPresence(Status_offline, Type_unavailable, sid+"@"+XmppJidComponent, "", "") + delete(g.FriendSteamId, sid) + } +} + +func (g *GatewayInfo) SendSteamMessage(steamId, message string) { + if !g.IsSteamConnected() { + log.Printf("%sTry to send message, but disconnected", LogSteamDebug) + return + } + + steamIdUint64, err := strconv.ParseUint(steamId, 10, 64) + if err == nil { + g.SteamClient.Social.SendMessage(steamid.SteamId(steamIdUint64), steamlang.EChatEntryType_ChatMsg, message) + } else { + log.Printf("%sFailed to get SteamId from %s", LogSteamError, steamId) + } +} + +func (g *GatewayInfo) SendSteamPresence(status steamlang.EPersonaState) { + if !g.IsSteamConnected() { + log.Printf("%sTry to send presence, but disconnected", LogSteamDebug) + return + } + g.SteamClient.Social.SetPersonaState(status) +} diff --git a/gateway/xmpp.go b/gateway/xmpp.go new file mode 100644 index 0000000..fc27900 --- /dev/null +++ b/gateway/xmpp.go @@ -0,0 +1,154 @@ +package gateway + +import ( + "git.kingpenguin.tk/chteufleur/go-xmpp.git" + "github.com/Philipp15b/go-steam/internal/steamlang" + + "log" + "strings" +) + +const ( + Status_online = "" + Status_offline = "" + Status_away = "away" + Status_chat = "chat" + Status_do_not_disturb = "dnd" + Status_extended_away = "xa" + + Type_available = "" + Type_unavailable = "unavailable" + Type_subscribe = "subscribe" + Type_subscribed = "subscribed" + Type_unsubscribe = "unsubscribe" + Type_unsubscribed = "unsubscribed" + Type_probe = "probe" + Type_error = "error" + + ActionConnexion = "action_xmpp_connexion" + ActionDeconnexion = "action_xmpp_deconnexion" + ActionMainMethodEnded = "action_xmpp_main_method_ended" + + LogXmppInfo = "\t[XMPP INFO]\t" + LogXmppError = "\t[XMPP ERROR]\t" + LogXmppDebug = "\t[XMPP DEBUG]\t" +) + +var ( + XmppJidComponent = "" +) + +func (g *GatewayInfo) ReceivedXMPP_Presence(presence *xmpp.Presence) { + if presence.Type == Type_probe || presence.Type == Type_error { + return + } + + transfertPresence := false + + if presence.Type == Type_subscribe { + // Send presence to tell that the JID has been added to roster + g.SendXmppPresence("", Type_subscribed, presence.To, g.XMPP_JID_Client, "") + + } else if presence.Type == Type_subscribed { + } else if presence.Type == Type_unsubscribe { + } else if presence.Type == Type_unsubscribed { + } else if presence.To == XmppJidComponent { + // Destination is gateway itself + if presence.Type == Type_unavailable { + // Disconnect + // TODO multi client connected management + g.XMPP_Disconnect() + go g.SteamDisconnect() + } else if presence.Type == Type_available { + // TODO multi client connected management + go g.SteamConnect() + transfertPresence = true + } + + } else { + // Destination is Steam user + if presence.Type == Type_unavailable { + // Disconnect + // TODO multi client connected management + g.XMPP_Disconnect() + go g.SteamDisconnect() + } else if presence.Type == Type_available { + // TODO multi client connected management + go g.SteamConnect() + transfertPresence = true + } + } + + if transfertPresence { + // Transfert presence to Steam network + var steamStatus steamlang.EPersonaState + + switch presence.Show { + case Status_online: + steamStatus = State_Online + + case Status_away: + steamStatus = State_Away + + case Status_chat: + steamStatus = State_Online + + case Status_extended_away: + steamStatus = State_Snooze + + case Status_do_not_disturb: + steamStatus = State_Busy + } + + if g.IsSteamConnected() { + g.SendSteamPresence(steamStatus) + g.SendXmppPresence(presence.Show, presence.Type, "", VersionToSend, "") + } + } +} + +func (g *GatewayInfo) ReceivedXMPP_Message(message *xmpp.Message) { + steamID := strings.SplitN(message.To, "@", 2)[0] + // TODO add subject if exist to the message + g.SendSteamMessage(steamID, message.Body) +} + +func (g *GatewayInfo) XMPP_Disconnect() { + g.SendXmppPresence(Status_offline, Type_unavailable, "", "", "") +} + +func (g *GatewayInfo) SendXmppPresence(status, tpye, from, message, nick string) { + p := xmpp.Presence{To: g.XMPP_JID_Client} + + if status != "" { + p.Show = status + } + if tpye != "" { + p.Type = tpye + } + if message != "" { + p.Status = message + } + if nick != "" { + p.Nick = nick + } + if from == "" { + p.From = XmppJidComponent + } else { + p.From = from + } + + log.Printf("%sSend presence %v", LogXmppInfo, p) + g.XMPP_Out <- p +} + +func (g *GatewayInfo) SendXmppMessage(from, subject, message string) { + m := xmpp.Message{To: g.XMPP_JID_Client, From: from, Body: message, Type: "chat"} + + if subject != "" { + m.Subject = subject + } + + log.Printf("%sSend message %v", LogXmppInfo, m) + g.XMPP_Out <- m +} diff --git a/main.go b/main.go index 9dcad8f..3523459 100644 --- a/main.go +++ b/main.go @@ -1,10 +1,10 @@ package main import ( - "git.kingpenguin.tk/chteufleur/go-xmpp4steam.git/steam" + "git.kingpenguin.tk/chteufleur/go-xmpp4steam.git/database" + "git.kingpenguin.tk/chteufleur/go-xmpp4steam.git/gateway" "git.kingpenguin.tk/chteufleur/go-xmpp4steam.git/xmpp" - "github.com/Philipp15b/go-steam/internal/steamlang" "github.com/jimlawless/cfg" "log" @@ -15,16 +15,16 @@ import ( ) const ( - Version = "go-xmpp4steam v0.2.1" + Version = "go-xmpp4steam v0.3.0" configurationFilePath = "xmpp4steam.cfg" ) var ( - mapConfig = make(map[string]string) - SetSteamId = make(map[string]struct{}) + mapConfig = make(map[string]string) ) func init() { + gateway.VersionToSend = Version err := cfg.Load(configurationFilePath, mapConfig) if err != nil { log.Fatal("Failed to load configuration file.", err) @@ -34,27 +34,15 @@ func init() { xmpp.Addr = mapConfig["xmpp_server_address"] + ":" + mapConfig["xmpp_server_port"] xmpp.JidStr = mapConfig["xmpp_hostname"] xmpp.Secret = mapConfig["xmpp_secret"] - xmpp.PreferedJID = mapConfig["xmpp_authorized_jid"] xmpp.Debug = mapConfig["xmpp_debug"] == "true" - - // Steam config - steam.Username = mapConfig["steam_login"] - steam.Password = mapConfig["steam_password"] - steam.AuthCode = "" + gateway.XmppJidComponent = xmpp.JidStr } func main() { - go gatewayXmppSteamAction() - go gatewaySteamXmppAction() - - go gatewayXmppSteamPresence() - go gatewayXmppSteamMessage() - go gatewayXmppSteamAuthCode() - - go gatewaySteamXmppMessage() - go gatewaySteamXmppPresence() - - go steam.Run() + allDbUsers := database.GetAllLines() + for _, dbUser := range allDbUsers { + xmpp.AddNewUser(dbUser.Jid, dbUser.SteamLogin, dbUser.SteamPwd) + } go xmpp.Run() sigchan := make(chan os.Signal, 1) @@ -63,159 +51,8 @@ func main() { signal.Notify(sigchan, os.Kill) <-sigchan - steam.Disconnect() xmpp.Disconnect() time.Sleep(1 * time.Second) log.Println("Exit main()") } - -// XMPP -> Steam gateways -func gatewayXmppSteamAction() { - for { - action := <-xmpp.ChanAction - - switch action { - case xmpp.ActionConnexion: - if !steam.IsConnected() { - steam.Connect() - } - - case xmpp.ActionDeconnexion: - if steam.IsConnected() { - steam.Disconnect() - } - - case xmpp.ActionMainMethodEnded: - go xmpp.Run() - } - } -} - -func gatewayXmppSteamPresence() { - for { - status := <-xmpp.ChanPresence - tpye := <-xmpp.ChanPresence - - var steamStatus steamlang.EPersonaState - - switch status { - case xmpp.Status_online: - steamStatus = steam.State_Online - - case xmpp.Status_away: - steamStatus = steam.State_Away - - case xmpp.Status_chat: - - case xmpp.Status_extended_away: - steamStatus = steam.State_Snooze - - case xmpp.Status_do_not_disturb: - steamStatus = steam.State_Busy - } - - steam.SendPresence(steamStatus) - xmpp.SendPresence(status, tpye, Version) - } -} - -func gatewayXmppSteamMessage() { - for { - steamId := <-xmpp.ChanMessage - message := <-xmpp.ChanMessage - - steam.SendMessage(steamId, message) - } -} - -func gatewayXmppSteamAuthCode() { - for { - authCode := <-xmpp.ChanAuthCode - steam.AuthCode = authCode - steam.Disconnect() - time.Sleep(2 * time.Second) - go steam.Run() - } -} - -// /XMPP -> Steam gateways - -// Steam -> XMPP gateways -func gatewaySteamXmppAction() { - for { - action := <-steam.ChanAction - switch action { - case steam.ActionConnected: - xmpp.SendPresence(xmpp.CurrentStatus, xmpp.Type_available, Version) - - case steam.ActionDisconnected: - xmpp.Disconnect() - disconnectAllSteamUser() - - case steam.ActionFatalError: - disconnectAllSteamUser() - time.Sleep(2 * time.Second) - go steam.Run() - - case steam.ActionMainMethodEnded: - go steam.Run() - } - } -} - -func gatewaySteamXmppMessage() { - for { - steamId := <-steam.ChanMessage - message := <-steam.ChanMessage - xmpp.SendMessage(steamId+"@"+xmpp.JidStr, message) - } -} - -func gatewaySteamXmppPresence() { - for { - steamId := <-steam.ChanPresence - name := <-steam.ChanPresence - stat := <-steam.ChanPresenceSteam - gameName := <-steam.ChanPresence - - var status string - var tpye string - switch stat { - case steam.State_Offline: - status = xmpp.Status_offline - tpye = xmpp.Type_unavailable - - case steam.State_Online: - status = xmpp.Status_online - tpye = xmpp.Type_available - - case steam.State_Busy: - status = xmpp.Status_do_not_disturb - tpye = xmpp.Type_available - - case steam.State_Away: - status = xmpp.Status_away - tpye = xmpp.Type_available - - case steam.State_Snooze: - status = xmpp.Status_extended_away - tpye = xmpp.Type_available - } - - if _, ok := SetSteamId[steamId]; !ok { - xmpp.SendPresenceFrom(status, xmpp.Type_subscribe, steamId+"@"+xmpp.JidStr, gameName, name) - SetSteamId[steamId] = struct{}{} - } - xmpp.SendPresenceFrom(status, tpye, steamId+"@"+xmpp.JidStr, gameName, name) - } -} - -func disconnectAllSteamUser() { - for sid, _ := range SetSteamId { - xmpp.SendPresenceFrom(xmpp.Status_offline, xmpp.Type_unavailable, sid+"@"+xmpp.JidStr, "", "") - delete(SetSteamId, sid) - } -} - -// /Steam -> XMPP gateways diff --git a/servers.addr b/servers.addr index 60315e2..b35277d 100644 --- a/servers.addr +++ b/servers.addr @@ -1 +1 @@ -{"Addresses":[{"IP":"162.254.197.41","Port":27020},{"IP":"146.66.152.10","Port":27017},{"IP":"146.66.152.11","Port":27020},{"IP":"146.66.152.11","Port":27019},{"IP":"146.66.152.10","Port":27018},{"IP":"162.254.197.40","Port":27018},{"IP":"146.66.152.10","Port":27019},{"IP":"146.66.152.11","Port":27018},{"IP":"146.66.152.11","Port":27017},{"IP":"146.66.152.10","Port":27020},{"IP":"162.254.197.41","Port":27019},{"IP":"162.254.197.41","Port":27017},{"IP":"162.254.197.40","Port":27019},{"IP":"162.254.197.42","Port":27021},{"IP":"162.254.197.42","Port":27017},{"IP":"162.254.197.42","Port":27019},{"IP":"162.254.197.41","Port":27021},{"IP":"162.254.197.42","Port":27018},{"IP":"162.254.197.40","Port":27021},{"IP":"162.254.197.40","Port":27017},{"IP":"162.254.197.41","Port":27018},{"IP":"162.254.197.42","Port":27020},{"IP":"162.254.197.40","Port":27020},{"IP":"162.254.196.43","Port":27017},{"IP":"162.254.196.43","Port":27020},{"IP":"162.254.196.42","Port":27020},{"IP":"162.254.196.40","Port":27021},{"IP":"162.254.196.42","Port":27021},{"IP":"162.254.196.40","Port":27020},{"IP":"162.254.196.40","Port":27019},{"IP":"162.254.196.43","Port":27018},{"IP":"162.254.196.41","Port":27020},{"IP":"162.254.196.42","Port":27017},{"IP":"185.25.182.10","Port":27020},{"IP":"162.254.196.41","Port":27017},{"IP":"162.254.196.42","Port":27019},{"IP":"162.254.196.40","Port":27018},{"IP":"162.254.196.43","Port":27019},{"IP":"162.254.196.41","Port":27019},{"IP":"162.254.196.42","Port":27018},{"IP":"162.254.196.41","Port":27021},{"IP":"162.254.196.40","Port":27017},{"IP":"146.66.155.8","Port":27017},{"IP":"185.25.182.10","Port":27019},{"IP":"155.133.242.8","Port":27019},{"IP":"185.25.182.10","Port":27018},{"IP":"146.66.155.8","Port":27018},{"IP":"185.25.180.15","Port":27019},{"IP":"155.133.242.8","Port":27020},{"IP":"185.25.180.15","Port":27018},{"IP":"155.133.242.9","Port":27020},{"IP":"162.254.196.41","Port":27018},{"IP":"146.66.155.8","Port":27019},{"IP":"155.133.242.8","Port":27018},{"IP":"146.66.155.8","Port":27020},{"IP":"185.25.182.10","Port":27017},{"IP":"155.133.242.8","Port":27017},{"IP":"185.25.180.15","Port":27017},{"IP":"185.25.180.15","Port":27020},{"IP":"155.133.242.9","Port":27017},{"IP":"185.25.180.14","Port":27020},{"IP":"155.133.242.9","Port":27018},{"IP":"162.254.196.43","Port":27021},{"IP":"155.133.242.9","Port":27019},{"IP":"185.25.180.14","Port":27017},{"IP":"185.25.180.14","Port":27018},{"IP":"185.25.180.14","Port":27019},{"IP":"208.78.164.11","Port":27017},{"IP":"208.78.164.11","Port":27019},{"IP":"208.78.164.9","Port":27019},{"IP":"208.78.164.12","Port":27019},{"IP":"208.78.164.9","Port":27017},{"IP":"208.78.164.12","Port":27018},{"IP":"208.78.164.13","Port":27019},{"IP":"208.78.164.13","Port":27018},{"IP":"208.78.164.10","Port":27017},{"IP":"208.78.164.14","Port":27018},{"IP":"208.78.164.14","Port":27019},{"IP":"208.78.164.14","Port":27017},{"IP":"208.78.164.12","Port":27017}]} \ No newline at end of file +{"Addresses":[{"IP":"146.66.152.11","Port":27019},{"IP":"146.66.152.11","Port":27017},{"IP":"146.66.152.10","Port":27019},{"IP":"162.254.197.42","Port":27017},{"IP":"146.66.152.10","Port":27020},{"IP":"162.254.197.42","Port":27019},{"IP":"146.66.152.10","Port":27018},{"IP":"146.66.152.10","Port":27017},{"IP":"162.254.197.41","Port":27021},{"IP":"162.254.197.41","Port":27017},{"IP":"162.254.197.40","Port":27019},{"IP":"162.254.197.40","Port":27020},{"IP":"162.254.197.40","Port":27021},{"IP":"146.66.152.11","Port":27020},{"IP":"162.254.197.40","Port":27018},{"IP":"162.254.197.41","Port":27020},{"IP":"162.254.197.40","Port":27017},{"IP":"162.254.197.41","Port":27018},{"IP":"162.254.197.42","Port":27020},{"IP":"146.66.152.11","Port":27018},{"IP":"162.254.197.42","Port":27021},{"IP":"162.254.197.41","Port":27019},{"IP":"162.254.197.42","Port":27018},{"IP":"162.254.196.43","Port":27017},{"IP":"162.254.196.40","Port":27018},{"IP":"162.254.196.43","Port":27018},{"IP":"162.254.196.42","Port":27020},{"IP":"162.254.196.41","Port":27020},{"IP":"162.254.196.43","Port":27019},{"IP":"162.254.196.41","Port":27021},{"IP":"162.254.196.42","Port":27018},{"IP":"162.254.196.40","Port":27020},{"IP":"162.254.196.41","Port":27017},{"IP":"162.254.196.41","Port":27018},{"IP":"162.254.196.40","Port":27017},{"IP":"162.254.196.41","Port":27019},{"IP":"162.254.196.42","Port":27021},{"IP":"162.254.196.40","Port":27021},{"IP":"162.254.196.43","Port":27021},{"IP":"162.254.196.42","Port":27017},{"IP":"162.254.196.40","Port":27019},{"IP":"162.254.196.42","Port":27019},{"IP":"162.254.196.43","Port":27020},{"IP":"146.66.155.8","Port":27017},{"IP":"146.66.155.8","Port":27019},{"IP":"185.25.182.10","Port":27020},{"IP":"146.66.155.8","Port":27020},{"IP":"185.25.182.10","Port":27017},{"IP":"185.25.180.15","Port":27019},{"IP":"185.25.180.15","Port":27018},{"IP":"155.133.242.8","Port":27019},{"IP":"155.133.242.8","Port":27020},{"IP":"185.25.180.15","Port":27020},{"IP":"155.133.242.9","Port":27019},{"IP":"155.133.242.8","Port":27018},{"IP":"146.66.155.8","Port":27018},{"IP":"185.25.182.10","Port":27019},{"IP":"185.25.180.14","Port":27018},{"IP":"155.133.242.9","Port":27020},{"IP":"185.25.180.14","Port":27020},{"IP":"185.25.180.15","Port":27017},{"IP":"185.25.180.14","Port":27017},{"IP":"185.25.180.14","Port":27019},{"IP":"155.133.242.8","Port":27017},{"IP":"155.133.242.9","Port":27017},{"IP":"185.25.182.10","Port":27018},{"IP":"155.133.242.9","Port":27018},{"IP":"208.78.164.10","Port":27019},{"IP":"208.78.164.14","Port":27017},{"IP":"208.78.164.11","Port":27018},{"IP":"208.78.164.10","Port":27018},{"IP":"208.78.164.11","Port":27019},{"IP":"208.78.164.12","Port":27019},{"IP":"208.78.164.13","Port":27019},{"IP":"208.78.164.10","Port":27017},{"IP":"208.78.164.9","Port":27017},{"IP":"208.78.164.14","Port":27019},{"IP":"208.78.164.11","Port":27017},{"IP":"208.78.164.9","Port":27018},{"IP":"208.78.164.9","Port":27019}]} \ No newline at end of file diff --git a/steam/steam.go b/steam/steam.go deleted file mode 100644 index 917f540..0000000 --- a/steam/steam.go +++ /dev/null @@ -1,168 +0,0 @@ -package steam - -import ( - "github.com/Philipp15b/go-steam" - "github.com/Philipp15b/go-steam/internal/steamlang" - "github.com/Philipp15b/go-steam/steamid" - - "encoding/json" - "io/ioutil" - "log" - "strconv" - "time" -) - -const ( - sentryFile = "sentry" - serverAddrs = "servers.addr" - - State_Offline = steamlang.EPersonaState_Offline - State_Online = steamlang.EPersonaState_Online - State_Busy = steamlang.EPersonaState_Busy - State_Away = steamlang.EPersonaState_Away - State_Snooze = steamlang.EPersonaState_Snooze - State_LookingToTrade = steamlang.EPersonaState_LookingToTrade - State_LookingToPlay = steamlang.EPersonaState_LookingToPlay - State_Max = steamlang.EPersonaState_Max - - ActionConnected = "steam_connected" - ActionDisconnected = "steam_disconnected" - ActionFatalError = "steam_fatal_error" - ActionMainMethodEnded = "action_steam_main_method_ended" - - LogInfo = "\t[STEAM INFO]\t" - LogError = "\t[STEAM ERROR]\t" - LogDebug = "\t[STEAM DEBUG]\t" -) - -var ( - Username = "" - Password = "" - AuthCode = "" - - myLoginInfo = new(steam.LogOnDetails) - client = steam.NewClient() - - ChanPresence = make(chan string) - ChanPresenceSteam = make(chan steamlang.EPersonaState) - ChanMessage = make(chan string) - ChanAction = make(chan string) -) - -func Run() { - log.Printf("%sRunning", LogInfo) - setLoginInfos() - client = steam.NewClient() - client.ConnectionTimeout = 10 * time.Second - - mainSteam() - ChanAction <- ActionMainMethodEnded -} - -func mainSteam() { - for event := range client.Events() { - switch e := event.(type) { - case *steam.ConnectedEvent: - client.Auth.LogOn(myLoginInfo) - - case *steam.MachineAuthUpdateEvent: - ioutil.WriteFile(sentryFile, e.Hash, 0666) - - case *steam.LoggedOnEvent: - SendPresence(steamlang.EPersonaState_Online) - ChanAction <- ActionConnected - - case steam.FatalErrorEvent: - log.Printf("%sFatalError: ", LogError, e) - ChanAction <- ActionFatalError - return - - case error: - log.Printf("%s", LogError, e) - - case *steam.ClientCMListEvent: - // Save servers addresses - b, err := json.Marshal(*e) - if err != nil { - log.Printf("%sFailed to json.Marshal() servers list", LogError) - } else { - ioutil.WriteFile(serverAddrs, b, 0666) - } - - case *steam.PersonaStateEvent: - ChanPresence <- e.FriendId.ToString() - ChanPresence <- e.Name - ChanPresenceSteam <- e.State - ChanPresence <- e.GameName - - case *steam.ChatMsgEvent: - ChanMessage <- e.ChatterId.ToString() - ChanMessage <- e.Message - - default: - log.Printf("%s", LogDebug, e) - } - } -} - -func setLoginInfos() { - var sentryHash steam.SentryHash - sentryHash, err := ioutil.ReadFile(sentryFile) - - myLoginInfo.Username = Username - myLoginInfo.Password = Password - - if err == nil { - myLoginInfo.SentryFileHash = sentryHash - log.Printf("%sAuthentification by SentryFileHash", LogDebug) - } else if AuthCode != "" { - myLoginInfo.AuthCode = AuthCode - log.Printf("%sAuthentification by AuthCode (%s)", LogDebug, AuthCode) - } else { - log.Printf("%sFirst authentification", LogDebug) - } -} - -func IsConnected() bool { - return client.Connected() -} - -func Connect() { - if IsConnected() { - log.Printf("%sTry to connect, but already connected", LogDebug) - return - } - - b, err := ioutil.ReadFile(serverAddrs) - if err == nil { - var toList steam.ClientCMListEvent - err := json.Unmarshal(b, &toList) - if err != nil { - log.Printf("%sFailed to json.Unmarshal() servers list", LogError) - } else { - log.Printf("%sConnecting...", LogInfo) - client.ConnectTo(toList.Addresses[0]) - } - } else { - log.Printf("%sFailed to read servers list file", LogError) - client.Connect() - } -} - -func Disconnect() { - log.Printf("%sSteam disconnect", LogInfo) - go client.Disconnect() -} - -func SendMessage(steamId, message string) { - steamIdUint64, err := strconv.ParseUint(steamId, 10, 64) - if err == nil { - client.Social.SendMessage(steamid.SteamId(steamIdUint64), steamlang.EChatEntryType_ChatMsg, message) - } else { - log.Printf("%sFailed to get SteamId from %s", LogError, steamId) - } -} - -func SendPresence(status steamlang.EPersonaState) { - client.Social.SetPersonaState(status) -} diff --git a/xmpp/commands.go b/xmpp/commands.go index b611983..30c5a81 100644 --- a/xmpp/commands.go +++ b/xmpp/commands.go @@ -2,12 +2,15 @@ package xmpp import ( "git.kingpenguin.tk/chteufleur/go-xmpp.git" + "git.kingpenguin.tk/chteufleur/go-xmpp4steam.git/database" "log" + "strings" ) const ( - CommandAuthcode = "steamAuthCodeCommand" + CommandAuthcode = "steamAuthCodeCommand" + CommandGetIdentifiants = "steamGetIdentifiants" ) var ( @@ -20,7 +23,9 @@ func execDiscoCommand(iq *xmpp.Iq) { discoItem := &xmpp.DiscoItems{Node: xmpp.NodeAdHocCommand} // Add available commands - discoI := &xmpp.DiscoItem{JID: jid.Domain, Node: CommandAuthcode, Name: "Add Auth Code"} + discoI := &xmpp.DiscoItem{JID: jid.Domain, Node: CommandAuthcode, Name: "Add Steam Auth Code"} + discoItem.Item = append(discoItem.Item, *discoI) + discoI = &xmpp.DiscoItem{JID: jid.Domain, Node: CommandGetIdentifiants, Name: "Steam registration"} discoItem.Item = append(discoItem.Item, *discoI) reply.PayloadEncode(discoItem) @@ -45,6 +50,16 @@ func execCommandAdHoc(iq *xmpp.Iq) { cmdXForm.Fields = append(cmdXForm.Fields, *field) cmd.XForm = *cmdXForm + } else if adHoc.Node == CommandGetIdentifiants { + // Command Auth Code + cmdXForm := &xmpp.AdHocXForm{Type: xmpp.TypeAdHocForm, Title: "Steam Account Info", Instructions: "Please provide your Steam login and password."} + + field := &xmpp.AdHocField{Var: "login", Label: "Steam Login", Type: xmpp.TypeAdHocFieldTextSingle} + cmdXForm.Fields = append(cmdXForm.Fields, *field) + field = &xmpp.AdHocField{Var: "password", Label: "Steam Password", Type: xmpp.TypeAdHocFieldTextSingle} + cmdXForm.Fields = append(cmdXForm.Fields, *field) + + cmd.XForm = *cmdXForm } reply.PayloadEncode(cmd) comp.Out <- reply @@ -55,7 +70,7 @@ func execCommandAdHoc(iq *xmpp.Iq) { cmd := &xmpp.AdHocCommand{Node: adHoc.Node, Status: xmpp.StatusAdHocCompleted, SessionId: adHoc.SessionId} if adHoc.Node == CommandAuthcode && adHoc.XForm.Type == xmpp.TypeAdHocSubmit { - cmdXForm := &xmpp.AdHocXForm{Type: xmpp.TypeAdHocResult, Title: "Steam Auth Code "} + cmdXForm := &xmpp.AdHocXForm{Type: xmpp.TypeAdHocResult, Title: "Steam Auth Code"} cmd.XForm = *cmdXForm note := &xmpp.AdHocNote{Type: xmpp.TypeAdHocNoteInfo} @@ -70,11 +85,54 @@ func execCommandAdHoc(iq *xmpp.Iq) { } if authCode != "" { // Succeded - ChanAuthCode <- authCode - note.Value = "Commande effectuée avec succes !" + jidBare := strings.SplitN(iq.From, "/", 2)[0] + g := MapGatewayInfo[jidBare] + if g != nil { + g.SetSteamAuthCode(authCode) + note.Value = "Command succeded !" + } else { + note.Value = "Your are not registred. Please, register before sending Steam auth code." + } } else { // Failed - note.Value = "Une erreur c'est produite à l'exécution de la commande…" + note.Value = "Error append while executing command" + } + cmd.Note = *note + + } else if adHoc.Node == CommandGetIdentifiants { + cmdXForm := &xmpp.AdHocXForm{Type: xmpp.TypeAdHocResult, Title: "Steam Account Info"} + cmd.XForm = *cmdXForm + note := &xmpp.AdHocNote{Type: xmpp.TypeAdHocNoteInfo} + + // Command Auth Code + steamLogin := "" + steamPwd := "" + fields := adHoc.XForm.Fields + for _, field := range fields { + if field.Var == "login" { + steamLogin = field.Value + } else if field.Var == "password" { + steamPwd = field.Value + } + } + if steamLogin != "" && steamPwd != "" { + // Succeded + jidBare := strings.SplitN(iq.From, "/", 2)[0] + dbUser := new(database.DatabaseLine) + dbUser.Jid = jidBare + dbUser.SteamLogin = steamLogin + dbUser.SteamPwd = steamPwd + + // TODO update + if dbUser.AddLine() { + AddNewUser(dbUser.Jid, dbUser.SteamLogin, dbUser.SteamPwd) + note.Value = "Command succeded !" + } else { + note.Value = "Error append while executing command" + } + } else { + // Failed + note.Value = "Failed because Steam login or Steam password is empty." } cmd.Note = *note } diff --git a/xmpp/xmpp.go b/xmpp/xmpp.go index 0d4732b..52aa676 100644 --- a/xmpp/xmpp.go +++ b/xmpp/xmpp.go @@ -2,35 +2,20 @@ package xmpp import ( "git.kingpenguin.tk/chteufleur/go-xmpp.git" + "git.kingpenguin.tk/chteufleur/go-xmpp4steam.git/gateway" "log" "strings" ) const ( - Status_online = "" - Status_offline = "" - Status_away = "away" - Status_chat = "chat" - Status_do_not_disturb = "dnd" - Status_extended_away = "xa" - - Type_available = "" - Type_unavailable = "unavailable" - Type_subscribe = "subscribe" - Type_subscribed = "subscribed" - Type_unsubscribe = "unsubscribe" - Type_unsubscribed = "unsubscribed" - Type_probe = "probe" - Type_error = "error" - ActionConnexion = "action_xmpp_connexion" ActionDeconnexion = "action_xmpp_deconnexion" ActionMainMethodEnded = "action_xmpp_main_method_ended" - LogInfo = "\t[XMPP INFO]\t" - LogError = "\t[XMPP ERROR]\t" - LogDebug = "\t[XMPP DEBUG]\t" + LogInfo = "\t[XMPP COMPONENT INFO]\t" + LogError = "\t[XMPP COMPONENT ERROR]\t" + LogDebug = "\t[XMPP COMPONENT DEBUG]\t" ) var ( @@ -38,21 +23,15 @@ var ( JidStr = "" Secret = "" - PreferedJID = "" - jid xmpp.JID stream = new(xmpp.Stream) comp = new(xmpp.XMPP) - ChanPresence = make(chan string) - ChanMessage = make(chan string) - ChanAction = make(chan string) - - CurrentStatus = Status_offline - - setJIDconnected = make(map[string]bool) + ChanAction = make(chan string) Debug = true + + MapGatewayInfo = make(map[string]*gateway.GatewayInfo) ) func Run() { @@ -67,35 +46,34 @@ func Run() { } func mainXMPP() { + // Define xmpp out for all users + for _, u := range MapGatewayInfo { + u.XMPP_Out = comp.Out + } + for x := range comp.In { switch v := x.(type) { case *xmpp.Presence: - if strings.SplitN(v.From, "/", 2)[0] == PreferedJID && v.Type != Type_probe { - if v.Type == Type_unavailable && v.To == JidStr { - delete(setJIDconnected, v.From) - log.Printf("%sPresence reçut unavailable", LogDebug) - if len(setJIDconnected) <= 0 { - // Disconnect only when all JID are disconnected - Disconnect() - ChanAction <- ActionDeconnexion - } - } else if v.Type == Type_subscribe { - SendPresenceFrom("", Type_subscribed, JidStr, "", "") - } else if v.Type != Type_subscribed { // Type subscribed is send by JID without ressourse - log.Printf("%sAdd connected user. JID : %s", LogInfo, v.From) - setJIDconnected[v.From] = true - CurrentStatus = v.Show - ChanAction <- ActionConnexion + jidBare := strings.SplitN(v.From, "/", 2)[0] + g := MapGatewayInfo[jidBare] + if g != nil { + log.Printf("%sPresence transfered to %s", LogDebug, jidBare) + g.ReceivedXMPP_Presence(v) + } else { + if v.Type != gateway.Type_error && v.Type != gateway.Type_probe { + SendPresence(gateway.Status_offline, gateway.Type_unavailable, jid.Domain, v.From, "Your are not registred", "") } - - ChanPresence <- v.Show - ChanPresence <- v.Type } case *xmpp.Message: - steamID := strings.SplitN(v.To, "@", 2)[0] - ChanMessage <- steamID - ChanMessage <- v.Body + jidBare := strings.SplitN(v.From, "/", 2)[0] + g := MapGatewayInfo[jidBare] + if g != nil { + log.Printf("%sMessage transfered to %s", LogDebug, jidBare) + g.ReceivedXMPP_Message(v) + } else { + SendMessage(v.From, "", "Your are not registred. If you want to register, please, send an Ad-Hoc command.") + } case *xmpp.Iq: switch v.PayloadName().Space { @@ -112,7 +90,7 @@ func mainXMPP() { } // Send deconnexion - SendPresence(Status_offline, Type_unavailable, "") + SendPresence(gateway.Status_offline, gateway.Type_unavailable, "", "", "", "") } func must(v interface{}, err error) interface{} { @@ -124,26 +102,11 @@ func must(v interface{}, err error) interface{} { func Disconnect() { log.Printf("%sXMPP disconnect", LogInfo) - SendPresence(Status_offline, Type_unavailable, "") + SendPresence(gateway.Status_offline, gateway.Type_unavailable, "", "", "", "") } -func SendPresence(status, tpye, message string) { - comp.Out <- xmpp.Presence{To: PreferedJID, From: jid.Domain, Show: status, Type: tpye, Status: message} -} - -func SendPresenceFrom(status, tpye, from, message, nick string) { - /* - if message == "" { - comp.Out <- xmpp.Presence{To: PreferedJID, From: from, Show: status, Type: tpye, Nick: nick} - } else if nick == "" { - comp.Out <- xmpp.Presence{To: PreferedJID, From: from, Show: status, Type: tpye, Status: message} - } else if nick == "" { - comp.Out <- xmpp.Presence{To: PreferedJID, From: from, Show: status, Type: tpye} - } else { - comp.Out <- xmpp.Presence{To: PreferedJID, From: from, Show: status, Type: tpye, Status: message, Nick: nick} - } - */ - p := xmpp.Presence{To: PreferedJID, From: from} +func SendPresence(status, tpye, from, to, message, nick string) { + p := xmpp.Presence{} if status != "" { p.Show = status @@ -157,10 +120,40 @@ func SendPresenceFrom(status, tpye, from, message, nick string) { if nick != "" { p.Nick = nick } + if from == "" { + p.From = jid.Domain + } else { + p.From = from + } + if to != "" { + p.To = to + } comp.Out <- p } -func SendMessage(from, message string) { - comp.Out <- xmpp.Message{To: PreferedJID, From: from, Body: message} +func SendMessage(to, subject, message string) { + m := xmpp.Message{From: jid.Domain, To: to, Body: message, Type: "chat"} + + if subject != "" { + m.Subject = subject + } + + log.Printf("%sSenp message %v", LogInfo, m) + comp.Out <- m +} + +func AddNewUser(jid, steamLogin, steamPwd string) { + log.Printf("%sAdd user %s to the map", LogInfo, jid) + + // TODO Move Gateway creation into right package + g := new(gateway.GatewayInfo) + g.SteamLogin = steamLogin + g.SteamPassword = steamPwd + g.XMPP_JID_Client = jid + g.SentryFile = gateway.SentryDirectory + jid + g.FriendSteamId = make(map[string]struct{}) + + MapGatewayInfo[jid] = g + go g.Run() } diff --git a/xmpp4steam.cfg b/xmpp4steam.cfg index 0efbbf8..72ffb10 100644 --- a/xmpp4steam.cfg +++ b/xmpp4steam.cfg @@ -3,9 +3,4 @@ xmpp_server_address=192.168.1.2 xmpp_server_port=5347 xmpp_hostname=xmppsteam.kingpenguin.tk xmpp_secret=xmpp4steam_password -xmpp_authorized_jid=chteufleur@kingpenguin.tk xmpp_debug=true - -# Steam informations -steam_login=toto -steam_password=toto_password123$