diff options
Diffstat (limited to 'vendor/github.com/Philipp15b/go-steam/social.go')
-rw-r--r-- | vendor/github.com/Philipp15b/go-steam/social.go | 624 |
1 files changed, 624 insertions, 0 deletions
diff --git a/vendor/github.com/Philipp15b/go-steam/social.go b/vendor/github.com/Philipp15b/go-steam/social.go new file mode 100644 index 00000000..c61899cf --- /dev/null +++ b/vendor/github.com/Philipp15b/go-steam/social.go @@ -0,0 +1,624 @@ +package steam + +import ( + "bytes" + "encoding/binary" + "encoding/hex" + . "github.com/Philipp15b/go-steam/protocol" + . "github.com/Philipp15b/go-steam/protocol/protobuf" + . "github.com/Philipp15b/go-steam/protocol/steamlang" + . "github.com/Philipp15b/go-steam/rwu" + "github.com/Philipp15b/go-steam/socialcache" + . "github.com/Philipp15b/go-steam/steamid" + "github.com/golang/protobuf/proto" + "io" + "sync" + "time" +) + +// Provides access to social aspects of Steam. +type Social struct { + mutex sync.RWMutex + + name string + avatar string + personaState EPersonaState + + Friends *socialcache.FriendsList + Groups *socialcache.GroupsList + Chats *socialcache.ChatsList + + client *Client +} + +func newSocial(client *Client) *Social { + return &Social{ + Friends: socialcache.NewFriendsList(), + Groups: socialcache.NewGroupsList(), + Chats: socialcache.NewChatsList(), + client: client, + } +} + +// Gets the local user's avatar +func (s *Social) GetAvatar() string { + s.mutex.RLock() + defer s.mutex.RUnlock() + return s.avatar +} + +// Gets the local user's persona name +func (s *Social) GetPersonaName() string { + s.mutex.RLock() + defer s.mutex.RUnlock() + return s.name +} + +// Sets the local user's persona name and broadcasts it over the network +func (s *Social) SetPersonaName(name string) { + s.mutex.Lock() + defer s.mutex.Unlock() + s.name = name + s.client.Write(NewClientMsgProtobuf(EMsg_ClientChangeStatus, &CMsgClientChangeStatus{ + PersonaState: proto.Uint32(uint32(s.personaState)), + PlayerName: proto.String(name), + })) +} + +// Gets the local user's persona state +func (s *Social) GetPersonaState() EPersonaState { + s.mutex.RLock() + defer s.mutex.RUnlock() + return s.personaState +} + +// Sets the local user's persona state and broadcasts it over the network +func (s *Social) SetPersonaState(state EPersonaState) { + s.mutex.Lock() + defer s.mutex.Unlock() + s.personaState = state + s.client.Write(NewClientMsgProtobuf(EMsg_ClientChangeStatus, &CMsgClientChangeStatus{ + PersonaState: proto.Uint32(uint32(state)), + })) +} + +// Sends a chat message to ether a room or friend +func (s *Social) SendMessage(to SteamId, entryType EChatEntryType, message string) { + //Friend + if to.GetAccountType() == int32(EAccountType_Individual) || to.GetAccountType() == int32(EAccountType_ConsoleUser) { + s.client.Write(NewClientMsgProtobuf(EMsg_ClientFriendMsg, &CMsgClientFriendMsg{ + Steamid: proto.Uint64(to.ToUint64()), + ChatEntryType: proto.Int32(int32(entryType)), + Message: []byte(message), + })) + //Chat room + } else if to.GetAccountType() == int32(EAccountType_Clan) || to.GetAccountType() == int32(EAccountType_Chat) { + chatId := to.ClanToChat() + s.client.Write(NewClientMsg(&MsgClientChatMsg{ + ChatMsgType: entryType, + SteamIdChatRoom: chatId, + SteamIdChatter: s.client.SteamId(), + }, []byte(message))) + } +} + +// Adds a friend to your friends list or accepts a friend. You'll receive a FriendStateEvent +// for every new/changed friend +func (s *Social) AddFriend(id SteamId) { + s.client.Write(NewClientMsgProtobuf(EMsg_ClientAddFriend, &CMsgClientAddFriend{ + SteamidToAdd: proto.Uint64(id.ToUint64()), + })) +} + +// Removes a friend from your friends list +func (s *Social) RemoveFriend(id SteamId) { + s.client.Write(NewClientMsgProtobuf(EMsg_ClientRemoveFriend, &CMsgClientRemoveFriend{ + Friendid: proto.Uint64(id.ToUint64()), + })) +} + +// Ignores or unignores a friend on Steam +func (s *Social) IgnoreFriend(id SteamId, setIgnore bool) { + ignore := uint8(1) //True + if !setIgnore { + ignore = uint8(0) //False + } + s.client.Write(NewClientMsg(&MsgClientSetIgnoreFriend{ + MySteamId: s.client.SteamId(), + SteamIdFriend: id, + Ignore: ignore, + }, make([]byte, 0))) +} + +// Requests persona state for a list of specified SteamIds +func (s *Social) RequestFriendListInfo(ids []SteamId, requestedInfo EClientPersonaStateFlag) { + var friends []uint64 + for _, id := range ids { + friends = append(friends, id.ToUint64()) + } + s.client.Write(NewClientMsgProtobuf(EMsg_ClientRequestFriendData, &CMsgClientRequestFriendData{ + PersonaStateRequested: proto.Uint32(uint32(requestedInfo)), + Friends: friends, + })) +} + +// Requests persona state for a specified SteamId +func (s *Social) RequestFriendInfo(id SteamId, requestedInfo EClientPersonaStateFlag) { + s.RequestFriendListInfo([]SteamId{id}, requestedInfo) +} + +// Requests profile information for a specified SteamId +func (s *Social) RequestProfileInfo(id SteamId) { + s.client.Write(NewClientMsgProtobuf(EMsg_ClientFriendProfileInfo, &CMsgClientFriendProfileInfo{ + SteamidFriend: proto.Uint64(id.ToUint64()), + })) +} + +// Requests all offline messages and marks them as read +func (s *Social) RequestOfflineMessages() { + s.client.Write(NewClientMsgProtobuf(EMsg_ClientFSGetFriendMessageHistoryForOfflineMessages, &CMsgClientFSGetFriendMessageHistoryForOfflineMessages{})) +} + +// Attempts to join a chat room +func (s *Social) JoinChat(id SteamId) { + chatId := id.ClanToChat() + s.client.Write(NewClientMsg(&MsgClientJoinChat{ + SteamIdChat: chatId, + }, make([]byte, 0))) +} + +// Attempts to leave a chat room +func (s *Social) LeaveChat(id SteamId) { + chatId := id.ClanToChat() + payload := new(bytes.Buffer) + binary.Write(payload, binary.LittleEndian, s.client.SteamId().ToUint64()) // ChatterActedOn + binary.Write(payload, binary.LittleEndian, uint32(EChatMemberStateChange_Left)) // StateChange + binary.Write(payload, binary.LittleEndian, s.client.SteamId().ToUint64()) // ChatterActedBy + s.client.Write(NewClientMsg(&MsgClientChatMemberInfo{ + SteamIdChat: chatId, + Type: EChatInfoType_StateChange, + }, payload.Bytes())) +} + +// Kicks the specified chat member from the given chat room +func (s *Social) KickChatMember(room SteamId, user SteamId) { + chatId := room.ClanToChat() + s.client.Write(NewClientMsg(&MsgClientChatAction{ + SteamIdChat: chatId, + SteamIdUserToActOn: user, + ChatAction: EChatAction_Kick, + }, make([]byte, 0))) +} + +// Bans the specified chat member from the given chat room +func (s *Social) BanChatMember(room SteamId, user SteamId) { + chatId := room.ClanToChat() + s.client.Write(NewClientMsg(&MsgClientChatAction{ + SteamIdChat: chatId, + SteamIdUserToActOn: user, + ChatAction: EChatAction_Ban, + }, make([]byte, 0))) +} + +// Unbans the specified chat member from the given chat room +func (s *Social) UnbanChatMember(room SteamId, user SteamId) { + chatId := room.ClanToChat() + s.client.Write(NewClientMsg(&MsgClientChatAction{ + SteamIdChat: chatId, + SteamIdUserToActOn: user, + ChatAction: EChatAction_UnBan, + }, make([]byte, 0))) +} + +func (s *Social) HandlePacket(packet *Packet) { + switch packet.EMsg { + case EMsg_ClientPersonaState: + s.handlePersonaState(packet) + case EMsg_ClientClanState: + s.handleClanState(packet) + case EMsg_ClientFriendsList: + s.handleFriendsList(packet) + case EMsg_ClientFriendMsgIncoming: + s.handleFriendMsg(packet) + case EMsg_ClientAccountInfo: + s.handleAccountInfo(packet) + case EMsg_ClientAddFriendResponse: + s.handleFriendResponse(packet) + case EMsg_ClientChatEnter: + s.handleChatEnter(packet) + case EMsg_ClientChatMsg: + s.handleChatMsg(packet) + case EMsg_ClientChatMemberInfo: + s.handleChatMemberInfo(packet) + case EMsg_ClientChatActionResult: + s.handleChatActionResult(packet) + case EMsg_ClientChatInvite: + s.handleChatInvite(packet) + case EMsg_ClientSetIgnoreFriendResponse: + s.handleIgnoreFriendResponse(packet) + case EMsg_ClientFriendProfileInfoResponse: + s.handleProfileInfoResponse(packet) + case EMsg_ClientFSGetFriendMessageHistoryResponse: + s.handleFriendMessageHistoryResponse(packet) + } +} + +func (s *Social) handleAccountInfo(packet *Packet) { + //Just fire the personainfo, Auth handles the callback + flags := EClientPersonaStateFlag_PlayerName | EClientPersonaStateFlag_Presence | EClientPersonaStateFlag_SourceID + s.RequestFriendInfo(s.client.SteamId(), EClientPersonaStateFlag(flags)) +} + +func (s *Social) handleFriendsList(packet *Packet) { + list := new(CMsgClientFriendsList) + packet.ReadProtoMsg(list) + var friends []SteamId + for _, friend := range list.GetFriends() { + steamId := SteamId(friend.GetUlfriendid()) + isClan := steamId.GetAccountType() == int32(EAccountType_Clan) + + if isClan { + rel := EClanRelationship(friend.GetEfriendrelationship()) + if rel == EClanRelationship_None { + s.Groups.Remove(steamId) + } else { + s.Groups.Add(socialcache.Group{ + SteamId: steamId, + Relationship: rel, + }) + + } + if list.GetBincremental() { + s.client.Emit(&GroupStateEvent{steamId, rel}) + } + } else { + rel := EFriendRelationship(friend.GetEfriendrelationship()) + if rel == EFriendRelationship_None { + s.Friends.Remove(steamId) + } else { + s.Friends.Add(socialcache.Friend{ + SteamId: steamId, + Relationship: rel, + }) + + } + if list.GetBincremental() { + s.client.Emit(&FriendStateEvent{steamId, rel}) + } + } + if !list.GetBincremental() { + friends = append(friends, steamId) + } + } + if !list.GetBincremental() { + s.RequestFriendListInfo(friends, EClientPersonaStateFlag_DefaultInfoRequest) + s.client.Emit(&FriendsListEvent{}) + } +} + +func (s *Social) handlePersonaState(packet *Packet) { + list := new(CMsgClientPersonaState) + packet.ReadProtoMsg(list) + flags := EClientPersonaStateFlag(list.GetStatusFlags()) + for _, friend := range list.GetFriends() { + id := SteamId(friend.GetFriendid()) + if id == s.client.SteamId() { //this is our client id + s.mutex.Lock() + if friend.GetPlayerName() != "" { + s.name = friend.GetPlayerName() + } + avatar := hex.EncodeToString(friend.GetAvatarHash()) + if ValidAvatar(avatar) { + s.avatar = avatar + } + s.mutex.Unlock() + } else if id.GetAccountType() == int32(EAccountType_Individual) { + if (flags & EClientPersonaStateFlag_PlayerName) == EClientPersonaStateFlag_PlayerName { + if friend.GetPlayerName() != "" { + s.Friends.SetName(id, friend.GetPlayerName()) + } + } + if (flags & EClientPersonaStateFlag_Presence) == EClientPersonaStateFlag_Presence { + avatar := hex.EncodeToString(friend.GetAvatarHash()) + if ValidAvatar(avatar) { + s.Friends.SetAvatar(id, avatar) + } + s.Friends.SetPersonaState(id, EPersonaState(friend.GetPersonaState())) + s.Friends.SetPersonaStateFlags(id, EPersonaStateFlag(friend.GetPersonaStateFlags())) + } + if (flags & EClientPersonaStateFlag_GameDataBlob) == EClientPersonaStateFlag_GameDataBlob { + s.Friends.SetGameAppId(id, friend.GetGamePlayedAppId()) + s.Friends.SetGameId(id, friend.GetGameid()) + s.Friends.SetGameName(id, friend.GetGameName()) + } + } else if id.GetAccountType() == int32(EAccountType_Clan) { + if (flags & EClientPersonaStateFlag_PlayerName) == EClientPersonaStateFlag_PlayerName { + if friend.GetPlayerName() != "" { + s.Groups.SetName(id, friend.GetPlayerName()) + } + } + if (flags & EClientPersonaStateFlag_Presence) == EClientPersonaStateFlag_Presence { + avatar := hex.EncodeToString(friend.GetAvatarHash()) + if ValidAvatar(avatar) { + s.Groups.SetAvatar(id, avatar) + } + } + } + s.client.Emit(&PersonaStateEvent{ + StatusFlags: flags, + FriendId: id, + State: EPersonaState(friend.GetPersonaState()), + StateFlags: EPersonaStateFlag(friend.GetPersonaStateFlags()), + GameAppId: friend.GetGamePlayedAppId(), + GameId: friend.GetGameid(), + GameName: friend.GetGameName(), + GameServerIp: friend.GetGameServerIp(), + GameServerPort: friend.GetGameServerPort(), + QueryPort: friend.GetQueryPort(), + SourceSteamId: SteamId(friend.GetSteamidSource()), + GameDataBlob: friend.GetGameDataBlob(), + Name: friend.GetPlayerName(), + Avatar: hex.EncodeToString(friend.GetAvatarHash()), + LastLogOff: friend.GetLastLogoff(), + LastLogOn: friend.GetLastLogon(), + ClanRank: friend.GetClanRank(), + ClanTag: friend.GetClanTag(), + OnlineSessionInstances: friend.GetOnlineSessionInstances(), + PublishedSessionId: friend.GetPublishedInstanceId(), + PersonaSetByUser: friend.GetPersonaSetByUser(), + FacebookName: friend.GetFacebookName(), + FacebookId: friend.GetFacebookId(), + }) + } +} + +func (s *Social) handleClanState(packet *Packet) { + body := new(CMsgClientClanState) + packet.ReadProtoMsg(body) + var name string + var avatar string + if body.GetNameInfo() != nil { + name = body.GetNameInfo().GetClanName() + avatar = hex.EncodeToString(body.GetNameInfo().GetShaAvatar()) + } + var totalCount, onlineCount, chattingCount, ingameCount uint32 + if body.GetUserCounts() != nil { + usercounts := body.GetUserCounts() + totalCount = usercounts.GetMembers() + onlineCount = usercounts.GetOnline() + chattingCount = usercounts.GetChatting() + ingameCount = usercounts.GetInGame() + } + var events, announcements []ClanEventDetails + for _, event := range body.GetEvents() { + events = append(events, ClanEventDetails{ + Id: event.GetGid(), + EventTime: event.GetEventTime(), + Headline: event.GetHeadline(), + GameId: event.GetGameId(), + JustPosted: event.GetJustPosted(), + }) + } + for _, announce := range body.GetAnnouncements() { + announcements = append(announcements, ClanEventDetails{ + Id: announce.GetGid(), + EventTime: announce.GetEventTime(), + Headline: announce.GetHeadline(), + GameId: announce.GetGameId(), + JustPosted: announce.GetJustPosted(), + }) + } + flags := EClientPersonaStateFlag(body.GetMUnStatusFlags()) + //Add stuff to group + clanid := SteamId(body.GetSteamidClan()) + if (flags & EClientPersonaStateFlag_PlayerName) == EClientPersonaStateFlag_PlayerName { + if name != "" { + s.Groups.SetName(clanid, name) + } + } + if (flags & EClientPersonaStateFlag_Presence) == EClientPersonaStateFlag_Presence { + if ValidAvatar(avatar) { + s.Groups.SetAvatar(clanid, avatar) + } + } + if body.GetUserCounts() != nil { + s.Groups.SetMemberTotalCount(clanid, totalCount) + s.Groups.SetMemberOnlineCount(clanid, onlineCount) + s.Groups.SetMemberChattingCount(clanid, chattingCount) + s.Groups.SetMemberInGameCount(clanid, ingameCount) + } + s.client.Emit(&ClanStateEvent{ + ClandId: clanid, + StateFlags: EClientPersonaStateFlag(body.GetMUnStatusFlags()), + AccountFlags: EAccountFlags(body.GetClanAccountFlags()), + ClanName: name, + Avatar: avatar, + MemberTotalCount: totalCount, + MemberOnlineCount: onlineCount, + MemberChattingCount: chattingCount, + MemberInGameCount: ingameCount, + Events: events, + Announcements: announcements, + }) +} + +func (s *Social) handleFriendResponse(packet *Packet) { + body := new(CMsgClientAddFriendResponse) + packet.ReadProtoMsg(body) + s.client.Emit(&FriendAddedEvent{ + Result: EResult(body.GetEresult()), + SteamId: SteamId(body.GetSteamIdAdded()), + PersonaName: body.GetPersonaNameAdded(), + }) +} + +func (s *Social) handleFriendMsg(packet *Packet) { + body := new(CMsgClientFriendMsgIncoming) + packet.ReadProtoMsg(body) + message := string(bytes.Split(body.GetMessage(), []byte{0x0})[0]) + s.client.Emit(&ChatMsgEvent{ + ChatterId: SteamId(body.GetSteamidFrom()), + Message: message, + EntryType: EChatEntryType(body.GetChatEntryType()), + Timestamp: time.Unix(int64(body.GetRtime32ServerTimestamp()), 0), + }) +} + +func (s *Social) handleChatMsg(packet *Packet) { + body := new(MsgClientChatMsg) + payload := packet.ReadClientMsg(body).Payload + message := string(bytes.Split(payload, []byte{0x0})[0]) + s.client.Emit(&ChatMsgEvent{ + ChatRoomId: SteamId(body.SteamIdChatRoom), + ChatterId: SteamId(body.SteamIdChatter), + Message: message, + EntryType: EChatEntryType(body.ChatMsgType), + }) +} + +func (s *Social) handleChatEnter(packet *Packet) { + body := new(MsgClientChatEnter) + payload := packet.ReadClientMsg(body).Payload + reader := bytes.NewBuffer(payload) + name, _ := ReadString(reader) + ReadByte(reader) //0 + count := body.NumMembers + chatId := SteamId(body.SteamIdChat) + clanId := SteamId(body.SteamIdClan) + s.Chats.Add(socialcache.Chat{SteamId: chatId, GroupId: clanId}) + for i := 0; i < int(count); i++ { + id, chatPerm, clanPerm := readChatMember(reader) + ReadBytes(reader, 6) //No idea what this is + s.Chats.AddChatMember(chatId, socialcache.ChatMember{ + SteamId: SteamId(id), + ChatPermissions: chatPerm, + ClanPermissions: clanPerm, + }) + } + s.client.Emit(&ChatEnterEvent{ + ChatRoomId: SteamId(body.SteamIdChat), + FriendId: SteamId(body.SteamIdFriend), + ChatRoomType: EChatRoomType(body.ChatRoomType), + OwnerId: SteamId(body.SteamIdOwner), + ClanId: SteamId(body.SteamIdClan), + ChatFlags: byte(body.ChatFlags), + EnterResponse: EChatRoomEnterResponse(body.EnterResponse), + Name: name, + }) +} + +func (s *Social) handleChatMemberInfo(packet *Packet) { + body := new(MsgClientChatMemberInfo) + payload := packet.ReadClientMsg(body).Payload + reader := bytes.NewBuffer(payload) + chatId := SteamId(body.SteamIdChat) + if body.Type == EChatInfoType_StateChange { + actedOn, _ := ReadUint64(reader) + state, _ := ReadInt32(reader) + actedBy, _ := ReadUint64(reader) + ReadByte(reader) //0 + stateChange := EChatMemberStateChange(state) + if stateChange == EChatMemberStateChange_Entered { + _, chatPerm, clanPerm := readChatMember(reader) + s.Chats.AddChatMember(chatId, socialcache.ChatMember{ + SteamId: SteamId(actedOn), + ChatPermissions: chatPerm, + ClanPermissions: clanPerm, + }) + } else if stateChange == EChatMemberStateChange_Banned || stateChange == EChatMemberStateChange_Kicked || + stateChange == EChatMemberStateChange_Disconnected || stateChange == EChatMemberStateChange_Left { + s.Chats.RemoveChatMember(chatId, SteamId(actedOn)) + } + stateInfo := StateChangeDetails{ + ChatterActedOn: SteamId(actedOn), + StateChange: EChatMemberStateChange(stateChange), + ChatterActedBy: SteamId(actedBy), + } + s.client.Emit(&ChatMemberInfoEvent{ + ChatRoomId: SteamId(body.SteamIdChat), + Type: EChatInfoType(body.Type), + StateChangeInfo: stateInfo, + }) + } +} + +func readChatMember(r io.Reader) (SteamId, EChatPermission, EClanPermission) { + ReadString(r) // MessageObject + ReadByte(r) // 7 + ReadString(r) //steamid + id, _ := ReadUint64(r) + ReadByte(r) // 2 + ReadString(r) //Permissions + chat, _ := ReadInt32(r) + ReadByte(r) // 2 + ReadString(r) //Details + clan, _ := ReadInt32(r) + return SteamId(id), EChatPermission(chat), EClanPermission(clan) +} + +func (s *Social) handleChatActionResult(packet *Packet) { + body := new(MsgClientChatActionResult) + packet.ReadClientMsg(body) + s.client.Emit(&ChatActionResultEvent{ + ChatRoomId: SteamId(body.SteamIdChat), + ChatterId: SteamId(body.SteamIdUserActedOn), + Action: EChatAction(body.ChatAction), + Result: EChatActionResult(body.ActionResult), + }) +} + +func (s *Social) handleChatInvite(packet *Packet) { + body := new(CMsgClientChatInvite) + packet.ReadProtoMsg(body) + s.client.Emit(&ChatInviteEvent{ + InvitedId: SteamId(body.GetSteamIdInvited()), + ChatRoomId: SteamId(body.GetSteamIdChat()), + PatronId: SteamId(body.GetSteamIdPatron()), + ChatRoomType: EChatRoomType(body.GetChatroomType()), + FriendChatId: SteamId(body.GetSteamIdFriendChat()), + ChatRoomName: body.GetChatName(), + GameId: body.GetGameId(), + }) +} + +func (s *Social) handleIgnoreFriendResponse(packet *Packet) { + body := new(MsgClientSetIgnoreFriendResponse) + packet.ReadClientMsg(body) + s.client.Emit(&IgnoreFriendEvent{ + Result: EResult(body.Result), + }) +} + +func (s *Social) handleProfileInfoResponse(packet *Packet) { + body := new(CMsgClientFriendProfileInfoResponse) + packet.ReadProtoMsg(body) + s.client.Emit(&ProfileInfoEvent{ + Result: EResult(body.GetEresult()), + SteamId: SteamId(body.GetSteamidFriend()), + TimeCreated: body.GetTimeCreated(), + RealName: body.GetRealName(), + CityName: body.GetCityName(), + StateName: body.GetStateName(), + CountryName: body.GetCountryName(), + Headline: body.GetHeadline(), + Summary: body.GetSummary(), + }) +} + +func (s *Social) handleFriendMessageHistoryResponse(packet *Packet) { + body := new(CMsgClientFSGetFriendMessageHistoryResponse) + packet.ReadProtoMsg(body) + steamid := SteamId(body.GetSteamid()) + for _, message := range body.GetMessages() { + if !message.GetUnread() { + continue // Skip already read messages + } + s.client.Emit(&ChatMsgEvent{ + ChatterId: steamid, + Message: message.GetMessage(), + EntryType: EChatEntryType_ChatMsg, + Timestamp: time.Unix(int64(message.GetTimestamp()), 0), + Offline: true, // GetUnread is true + }) + } +} |