go-xmpp4steam/gateway/xmpp.go

372 lines
10 KiB
Go

package gateway
import (
"git.kingpenguin.tk/chteufleur/go-xmpp.git/src/xmpp"
"git.kingpenguin.tk/chteufleur/go-xmpp4steam.git/logger"
"github.com/Philipp15b/go-steam/protocol/steamlang"
"strconv"
"strings"
"time"
)
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"
)
var (
XmppJidComponent = ""
iqId = uint64(0)
)
func NextIqId() string {
iqId += 1
ret := strconv.FormatUint(iqId, 10)
return ret
}
func (g *GatewayInfo) ReceivedXMPP_Presence(presence *xmpp.Presence) {
if presence.Type == Type_error {
return
}
transfertPresence := false
jid := strings.SplitN(presence.From, "/", 2)
steamJid := strings.SplitN(strings.SplitN(presence.To, "/", 2)[0], "@", 2)
if len(jid) == 2 {
// Resource exist —> client speaking
if presence.Type == Type_available {
g.SetXmppConnectedClient(presence.From)
} else if presence.Type == Type_unavailable {
g.RemoveXmppConnectedClient(presence.From)
}
}
if presence.Type == Type_probe {
steamFriendStatus := g.GetFriendSteamId(steamJid[0])
if steamFriendStatus != nil {
g.SendXmppPresence(steamFriendStatus.XMPP_Status, steamFriendStatus.XMPP_Type, "", steamJid[0]+"@"+XmppJidComponent, steamFriendStatus.SteamGameName, steamFriendStatus.SteamName)
}
} else if presence.Type == Type_subscribe {
// Send presence to tell that the JID has been added to roster
g.SendXmppPresence("", Type_subscribed, presence.From, 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
if g.GetLenXmppConnectedClient() <= 0 {
g.Disconnect()
}
} else {
if presence.Type == Type_available {
g.addUserIntoRoster(presence.To, "xmpp4steam gateway")
}
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, "", "", presence.Status, "")
}
}
}
func (g *GatewayInfo) ReceivedXMPP_Message(message *xmpp.Message) {
steamID := strings.SplitN(message.To, "@", 2)[0]
if message.Composing != nil {
g.SendSteamMessageComposing(steamID)
} else if message.Paused != nil {
return
} else if message.Inactive != nil {
return
} else if message.Gone != nil {
g.SendSteamMessageLeaveConversation(steamID)
} else {
if message.Body != nil && len(message.Body) != 0 {
g.SendSteamMessage(steamID, message.Body[0].Value)
}
}
}
func (g *GatewayInfo) ReceivedXMPP_IQ(iq *xmpp.IQ) bool {
ret := false
remoteRosterRequestValue := g.GetXmppRemoteRosterRequest(iq.ID)
if remoteRosterRequestValue == RemoteRosterRequestPermission {
g.RemoveXmppRemoteRosterRequest(iq.ID)
if iq.Type == xmpp.IQTypeError && iq.Error.Condition() == xmpp.ErrorForbidden {
g.AllowEditRoster = false
logger.Info.Printf("Set allow roster edition to %v", g.AllowEditRoster)
} else if iq.Type == xmpp.IQTypeSet && iq.PayloadName().Space == xmpp.NSRemoteRosterManager {
remoteRosterQuery := &xmpp.RemoteRosterManagerQuery{}
iq.PayloadDecode(remoteRosterQuery)
if remoteRosterQuery.Type == xmpp.RemoteRosterManagerTypeAllowed {
g.AllowEditRoster = true
} else if remoteRosterQuery.Type == xmpp.RemoteRosterManagerTypeRejected {
g.AllowEditRoster = false
} else {
g.AllowEditRoster = false
}
logger.Info.Printf("Set allow roster edition to %v", g.AllowEditRoster)
g.SendXmppMessage(XmppJidComponent, "", "Set allow roster edition to "+strconv.FormatBool(g.AllowEditRoster))
} else {
logger.Info.Printf("Check roster edition authorisation by querying roster's user")
// Remote roster namespace may not be supported (like prosody), so we send a roster query
iqId := NextIqId()
g.SetXmppRemoteRosterRequest(iqId, RemoteRosterRequestRoster)
iqSend := &xmpp.IQ{ID: iqId, Type: xmpp.IQTypeGet, From: iq.To, To: iq.From}
iqSend.PayloadEncode(&xmpp.RosterQuery{})
g.XMPP_Out <- iqSend
}
ret = true
} else if remoteRosterRequestValue == RemoteRosterRequestRoster {
g.RemoveXmppRemoteRosterRequest(iq.ID)
if iq.Type == xmpp.IQTypeResult && iq.PayloadName().Space == xmpp.NSRoster {
g.AllowEditRoster = true
} else {
g.AllowEditRoster = false
}
logger.Info.Printf("Set allow roster edition to %v", g.AllowEditRoster)
g.SendXmppMessage(XmppJidComponent, "", "Set allow roster edition to "+strconv.FormatBool(g.AllowEditRoster))
ret = true
}
return ret
}
func (g *GatewayInfo) XMPP_Disconnect() {
g.SendXmppPresence(Status_offline, Type_unavailable, "", "", "", "")
}
func (g *GatewayInfo) SendXmppPresence(status, tpye, to, from, message, nick string) {
p := xmpp.Presence{}
if status != "" {
p.Show = status
}
if tpye != "" {
p.Type = tpye
}
if message != "" {
p.Status = message
}
if nick != "" {
p.Nick = nick
}
if to == "" {
p.To = g.XMPP_JID_Client
} else {
p.To = to
}
if from == "" {
p.From = XmppJidComponent + "/" + resource
} else {
p.From = from + "/" + resource
}
if tpye == Type_subscribe && g.AllowEditRoster {
g.addUserIntoRoster(from, nick)
} else {
logger.Info.Printf("Send presence %v", p)
g.XMPP_Out <- p
}
}
func (g *GatewayInfo) addUserIntoRoster(jid, nick string) {
iq := xmpp.IQ{To: g.XMPP_JID_Client, Type: xmpp.IQTypeSet, ID: NextIqId()}
query := &xmpp.RosterQuery{}
queryItem := &xmpp.RosterItem{JID: jid, Name: nick, Subscription: xmpp.RosterSubscriptionBoth}
queryItem.Groupes = append(queryItem.Groupes, XmppGroupUser)
query.Items = append(query.Items, *queryItem)
iq.PayloadEncode(query)
logger.Info.Printf("Add user into roster %v", iq)
g.XMPP_Out <- iq
}
func (g *GatewayInfo) removeAllUserFromRoster() {
// Friends
for steamId := range g.SteamClient.Social.Friends.GetCopy() {
iq := xmpp.IQ{To: g.XMPP_JID_Client, Type: xmpp.IQTypeSet, ID: NextIqId()}
query := &xmpp.RosterQuery{}
query.Items = append(query.Items, *&xmpp.RosterItem{JID: steamId.ToString() + "@" + XmppJidComponent, Subscription: xmpp.RosterSubscriptionRemove})
iq.PayloadEncode(query)
logger.Info.Printf("Remove steam user roster")
g.XMPP_Out <- iq
}
// Myself
iq := xmpp.IQ{To: g.XMPP_JID_Client, Type: xmpp.IQTypeSet, ID: NextIqId()}
query := &xmpp.RosterQuery{}
query.Items = append(query.Items, *&xmpp.RosterItem{JID: g.SteamClient.SteamId().ToString() + "@" + XmppJidComponent, Subscription: xmpp.RosterSubscriptionRemove})
iq.PayloadEncode(query)
logger.Info.Printf("Remove steam user roster")
g.XMPP_Out <- iq
}
func (g *GatewayInfo) SendXmppMessage(from, subject, message string) {
g.sendXmppMessage(from, subject, message, &xmpp.Active{})
g.ChatstateNotificationData <- from
g.ChatstateNotificationData <- "stop"
g.ChatstateNotificationData <- from
g.ChatstateNotificationData <- "inactive"
}
func (g *GatewayInfo) SendXmppMessageLeaveConversation(from string) {
g.sendXmppMessage(from, "", "", &xmpp.Gone{})
g.ChatstateNotificationData <- from
g.ChatstateNotificationData <- "stop"
}
func (g *GatewayInfo) SendXmppMessageComposing(from string) {
g.sendXmppMessage(from, "", "", &xmpp.Composing{})
g.ChatstateNotificationData <- from
g.ChatstateNotificationData <- "paused"
g.ChatstateNotificationData <- from
g.ChatstateNotificationData <- "inactive"
}
func (g *GatewayInfo) chatstatesNotification() {
inactiveTimers := make(map[string]*time.Timer)
pausedTimers := make(map[string]*time.Timer)
g.ChatstateNotificationData = make(chan string)
for {
jid := <-g.ChatstateNotificationData
chatstate := <-g.ChatstateNotificationData
timerInactive, okInactive := inactiveTimers[jid]
timerPaused, okPaused := pausedTimers[jid]
switch chatstate {
case "stop":
if okInactive {
if timerInactive != nil {
if !timerInactive.Stop() {
<-timerInactive.C
}
delete(inactiveTimers, jid)
}
}
if okPaused {
if timerPaused != nil {
if !timerPaused.Stop() {
<-timerPaused.C
}
delete(pausedTimers, jid)
}
}
case "paused":
if okInactive {
if timerPaused != nil {
if !timerPaused.Stop() {
<-timerPaused.C
}
timerPaused.Reset(20 * time.Second)
}
} else {
timerPaused = time.AfterFunc(20*time.Second, func() {
g.sendXmppMessage(jid, "", "", &xmpp.Paused{})
delete(pausedTimers, jid)
})
pausedTimers[jid] = timerPaused
}
case "inactive":
if okInactive {
if timerInactive != nil {
if !timerInactive.Stop() {
<-timerInactive.C
}
timerInactive.Reset(120 * time.Second)
}
} else {
timerInactive = time.AfterFunc(120*time.Second, func() {
g.sendXmppMessage(jid, "", "", &xmpp.Inactive{})
delete(inactiveTimers, jid)
})
inactiveTimers[jid] = timerInactive
}
}
}
}
func (g *GatewayInfo) sendXmppMessage(from, subject, message string, chatState interface{}) {
if from != XmppJidComponent || from == XmppJidComponent && g.DebugMessage {
m := xmpp.Message{To: g.XMPP_JID_Client, From: from, Type: "chat"}
mBody := xmpp.MessageBody{Value: message}
m.Body = append(m.Body, mBody)
if subject != "" {
m.Subject = subject
}
switch v := chatState.(type) {
case *xmpp.Active:
m.Active = v
case *xmpp.Composing:
m.Composing = v
case *xmpp.Paused:
m.Paused = v
case *xmpp.Inactive:
m.Inactive = v
case *xmpp.Gone:
m.Gone = v
default:
m.Active = &xmpp.Active{}
}
logger.Info.Printf("Send message %v", m)
g.XMPP_Out <- m
}
}