2019-02-16 17:34:17 +01:00
|
|
|
// _ _ _ ____ _ _____
|
2017-11-18 17:27:06 +01:00
|
|
|
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
|
|
|
|
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
|
|
|
|
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
|
|
|
|
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
2019-01-14 19:11:17 +01:00
|
|
|
// |
|
2020-02-01 23:33:35 +01:00
|
|
|
// Copyright 2015-2020 Łukasz "JustArchi" Domeradzki
|
2018-07-27 04:52:14 +02:00
|
|
|
// Contact: JustArchi@JustArchi.net
|
2019-01-14 19:11:17 +01:00
|
|
|
// |
|
2018-07-27 04:52:14 +02:00
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
|
// You may obtain a copy of the License at
|
2019-01-14 19:11:17 +01:00
|
|
|
// |
|
2018-07-27 04:52:14 +02:00
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
2019-01-14 19:11:17 +01:00
|
|
|
// |
|
2018-07-27 04:52:14 +02:00
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
|
// limitations under the License.
|
2015-10-28 19:21:27 +01:00
|
|
|
|
2016-03-06 21:49:34 +01:00
|
|
|
using System;
|
2015-11-01 16:46:02 +01:00
|
|
|
using System.Collections.Generic;
|
2019-10-13 17:21:40 +02:00
|
|
|
using System.Diagnostics.CodeAnalysis;
|
2015-11-01 16:46:02 +01:00
|
|
|
using System.IO;
|
2016-05-13 06:32:42 +02:00
|
|
|
using System.Linq;
|
2016-02-22 18:34:45 +01:00
|
|
|
using System.Net;
|
2016-03-06 21:49:34 +01:00
|
|
|
using System.Threading.Tasks;
|
2016-12-11 04:23:44 +01:00
|
|
|
using ArchiSteamFarm.CMsgs;
|
2017-12-01 00:49:19 +01:00
|
|
|
using ArchiSteamFarm.Localization;
|
2018-09-08 01:03:55 +02:00
|
|
|
using ArchiSteamFarm.NLog;
|
2019-01-10 22:33:07 +01:00
|
|
|
using JetBrains.Annotations;
|
2016-11-24 07:32:16 +01:00
|
|
|
using SteamKit2;
|
|
|
|
|
using SteamKit2.Internal;
|
2015-10-25 06:16:50 +01:00
|
|
|
|
|
|
|
|
namespace ArchiSteamFarm {
|
2018-10-29 21:43:44 +01:00
|
|
|
public sealed class ArchiHandler : ClientMsgHandler {
|
2016-12-26 22:07:49 +01:00
|
|
|
internal const byte MaxGamesPlayedConcurrently = 32; // This is limit introduced by Steam Network
|
|
|
|
|
|
2016-11-06 12:06:02 +01:00
|
|
|
private readonly ArchiLogger ArchiLogger;
|
2018-07-24 23:43:25 +02:00
|
|
|
private readonly SteamUnifiedMessages.UnifiedService<IChatRoom> UnifiedChatRoomService;
|
2018-07-25 17:11:19 +02:00
|
|
|
private readonly SteamUnifiedMessages.UnifiedService<IClanChatRooms> UnifiedClanChatRoomsService;
|
2018-11-19 17:16:35 +03:00
|
|
|
private readonly SteamUnifiedMessages.UnifiedService<IEcon> UnifiedEconService;
|
2018-07-24 23:43:25 +02:00
|
|
|
private readonly SteamUnifiedMessages.UnifiedService<IFriendMessages> UnifiedFriendMessagesService;
|
|
|
|
|
private readonly SteamUnifiedMessages.UnifiedService<IPlayer> UnifiedPlayerService;
|
2020-07-05 00:45:13 +02:00
|
|
|
private readonly SteamUnifiedMessages.UnifiedService<ITwoFactor> UnifiedTwoFactorService;
|
2016-04-08 04:30:51 +02:00
|
|
|
|
2017-12-17 11:31:42 +01:00
|
|
|
internal DateTime LastPacketReceived { get; private set; }
|
2017-01-23 00:34:48 +01:00
|
|
|
|
2020-08-22 21:41:01 +02:00
|
|
|
internal ArchiHandler(ArchiLogger archiLogger, SteamUnifiedMessages steamUnifiedMessages) {
|
2018-07-24 23:43:25 +02:00
|
|
|
if ((archiLogger == null) || (steamUnifiedMessages == null)) {
|
|
|
|
|
throw new ArgumentNullException(nameof(archiLogger) + " || " + nameof(steamUnifiedMessages));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ArchiLogger = archiLogger;
|
|
|
|
|
UnifiedChatRoomService = steamUnifiedMessages.CreateService<IChatRoom>();
|
2018-07-25 17:11:19 +02:00
|
|
|
UnifiedClanChatRoomsService = steamUnifiedMessages.CreateService<IClanChatRooms>();
|
2018-11-19 17:16:35 +03:00
|
|
|
UnifiedEconService = steamUnifiedMessages.CreateService<IEcon>();
|
2018-07-24 23:43:25 +02:00
|
|
|
UnifiedFriendMessagesService = steamUnifiedMessages.CreateService<IFriendMessages>();
|
|
|
|
|
UnifiedPlayerService = steamUnifiedMessages.CreateService<IPlayer>();
|
2020-07-05 00:45:13 +02:00
|
|
|
UnifiedTwoFactorService = steamUnifiedMessages.CreateService<ITwoFactor>();
|
2018-07-24 23:43:25 +02:00
|
|
|
}
|
2016-04-08 04:30:51 +02:00
|
|
|
|
2016-11-24 07:32:16 +01:00
|
|
|
public override void HandleMsg(IPacketMsg packetMsg) {
|
2020-08-22 21:41:01 +02:00
|
|
|
if ((packetMsg == null) || (Client == null)) {
|
2020-08-23 20:45:24 +02:00
|
|
|
throw new ArgumentNullException(nameof(packetMsg) + " || " + nameof(Client));
|
2020-04-18 16:54:57 +02:00
|
|
|
}
|
|
|
|
|
|
2017-01-23 00:46:44 +01:00
|
|
|
LastPacketReceived = DateTime.UtcNow;
|
2017-01-23 00:33:08 +01:00
|
|
|
|
2016-11-24 07:32:16 +01:00
|
|
|
switch (packetMsg.MsgType) {
|
2020-04-06 20:37:53 +02:00
|
|
|
case EMsg.ClientCommentNotifications:
|
2020-04-06 20:42:26 +02:00
|
|
|
ClientMsgProtobuf<CMsgClientCommentNotifications> commentNotifications = new ClientMsgProtobuf<CMsgClientCommentNotifications>(packetMsg);
|
|
|
|
|
Client.PostCallback(new UserNotificationsCallback(packetMsg.TargetJobID, commentNotifications.Body));
|
2020-04-06 20:37:53 +02:00
|
|
|
|
|
|
|
|
break;
|
2016-11-24 07:32:16 +01:00
|
|
|
case EMsg.ClientItemAnnouncements:
|
2020-04-06 20:42:26 +02:00
|
|
|
ClientMsgProtobuf<CMsgClientItemAnnouncements> itemAnnouncements = new ClientMsgProtobuf<CMsgClientItemAnnouncements>(packetMsg);
|
|
|
|
|
Client.PostCallback(new UserNotificationsCallback(packetMsg.TargetJobID, itemAnnouncements.Body));
|
2018-12-15 00:27:15 +01:00
|
|
|
|
2016-11-24 07:32:16 +01:00
|
|
|
break;
|
|
|
|
|
case EMsg.ClientPlayingSessionState:
|
2020-04-06 20:42:26 +02:00
|
|
|
ClientMsgProtobuf<CMsgClientPlayingSessionState> playingSessionState = new ClientMsgProtobuf<CMsgClientPlayingSessionState>(packetMsg);
|
|
|
|
|
Client.PostCallback(new PlayingSessionStateCallback(packetMsg.TargetJobID, playingSessionState.Body));
|
2018-12-15 00:27:15 +01:00
|
|
|
|
2016-11-24 07:32:16 +01:00
|
|
|
break;
|
|
|
|
|
case EMsg.ClientPurchaseResponse:
|
2020-04-06 20:42:26 +02:00
|
|
|
ClientMsgProtobuf<CMsgClientPurchaseResponse> purchaseResponse = new ClientMsgProtobuf<CMsgClientPurchaseResponse>(packetMsg);
|
|
|
|
|
Client.PostCallback(new PurchaseResponseCallback(packetMsg.TargetJobID, purchaseResponse.Body));
|
2018-12-15 00:27:15 +01:00
|
|
|
|
2016-11-24 07:32:16 +01:00
|
|
|
break;
|
|
|
|
|
case EMsg.ClientRedeemGuestPassResponse:
|
2020-04-06 20:42:26 +02:00
|
|
|
ClientMsgProtobuf<CMsgClientRedeemGuestPassResponse> redeemGuestPassResponse = new ClientMsgProtobuf<CMsgClientRedeemGuestPassResponse>(packetMsg);
|
|
|
|
|
Client.PostCallback(new RedeemGuestPassResponseCallback(packetMsg.TargetJobID, redeemGuestPassResponse.Body));
|
2018-12-15 00:27:15 +01:00
|
|
|
|
2016-11-24 07:32:16 +01:00
|
|
|
break;
|
|
|
|
|
case EMsg.ClientSharedLibraryLockStatus:
|
2020-04-06 20:42:26 +02:00
|
|
|
ClientMsgProtobuf<CMsgClientSharedLibraryLockStatus> sharedLibraryLockStatus = new ClientMsgProtobuf<CMsgClientSharedLibraryLockStatus>(packetMsg);
|
|
|
|
|
Client.PostCallback(new SharedLibraryLockStatusCallback(packetMsg.TargetJobID, sharedLibraryLockStatus.Body));
|
2018-12-15 00:27:15 +01:00
|
|
|
|
2016-11-24 07:32:16 +01:00
|
|
|
break;
|
|
|
|
|
case EMsg.ClientUserNotifications:
|
2020-04-06 20:42:26 +02:00
|
|
|
ClientMsgProtobuf<CMsgClientUserNotifications> userNotifications = new ClientMsgProtobuf<CMsgClientUserNotifications>(packetMsg);
|
|
|
|
|
Client.PostCallback(new UserNotificationsCallback(packetMsg.TargetJobID, userNotifications.Body));
|
2018-12-15 00:27:15 +01:00
|
|
|
|
2016-11-24 07:32:16 +01:00
|
|
|
break;
|
2018-06-11 21:40:52 +02:00
|
|
|
case EMsg.ClientVanityURLChangedNotification:
|
2020-04-06 20:42:26 +02:00
|
|
|
ClientMsgProtobuf<CMsgClientVanityURLChangedNotification> vanityURLChangedNotification = new ClientMsgProtobuf<CMsgClientVanityURLChangedNotification>(packetMsg);
|
|
|
|
|
Client.PostCallback(new VanityURLChangedCallback(packetMsg.TargetJobID, vanityURLChangedNotification.Body));
|
2018-12-15 00:27:15 +01:00
|
|
|
|
2018-06-11 21:40:52 +02:00
|
|
|
break;
|
2016-09-30 01:15:26 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-24 23:43:25 +02:00
|
|
|
internal void AckChatMessage(ulong chatGroupID, ulong chatID, uint timestamp) {
|
|
|
|
|
if ((chatGroupID == 0) || (chatID == 0) || (timestamp == 0)) {
|
2020-08-22 21:41:01 +02:00
|
|
|
throw new ArgumentNullException(nameof(chatGroupID) + " || " + nameof(chatID) + " || " + nameof(timestamp));
|
2018-07-24 23:43:25 +02:00
|
|
|
}
|
|
|
|
|
|
2018-09-27 00:17:52 +02:00
|
|
|
if (!Client.IsConnected) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-24 23:43:25 +02:00
|
|
|
CChatRoom_AckChatMessage_Notification request = new CChatRoom_AckChatMessage_Notification {
|
|
|
|
|
chat_group_id = chatGroupID,
|
|
|
|
|
chat_id = chatID,
|
|
|
|
|
timestamp = timestamp
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
UnifiedChatRoomService.SendMessage(x => x.AckChatMessage(request), true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal void AckMessage(ulong steamID, uint timestamp) {
|
2019-02-24 17:11:47 +01:00
|
|
|
if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || (timestamp == 0)) {
|
2020-08-22 21:41:01 +02:00
|
|
|
throw new ArgumentNullException(nameof(steamID) + " || " + nameof(timestamp));
|
2018-07-24 23:43:25 +02:00
|
|
|
}
|
|
|
|
|
|
2018-09-27 00:17:52 +02:00
|
|
|
if (!Client.IsConnected) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-24 23:43:25 +02:00
|
|
|
CFriendMessages_AckMessage_Notification request = new CFriendMessages_AckMessage_Notification {
|
|
|
|
|
steamid_partner = steamID,
|
|
|
|
|
timestamp = timestamp
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
UnifiedFriendMessagesService.SendMessage(x => x.AckMessage(request), true);
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-24 17:11:47 +01:00
|
|
|
internal void AcknowledgeClanInvite(ulong steamID, bool acceptInvite) {
|
|
|
|
|
if ((steamID == 0) || !new SteamID(steamID).IsClanAccount) {
|
2020-08-22 21:41:01 +02:00
|
|
|
throw new ArgumentNullException(nameof(steamID));
|
2016-12-15 01:56:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!Client.IsConnected) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-23 22:24:14 +02:00
|
|
|
ClientMsg<CMsgClientAcknowledgeClanInvite> request = new ClientMsg<CMsgClientAcknowledgeClanInvite> {
|
2017-07-03 12:14:23 +02:00
|
|
|
Body = {
|
2019-02-24 17:11:47 +01:00
|
|
|
ClanID = steamID,
|
2018-04-23 22:24:14 +02:00
|
|
|
AcceptInvite = acceptInvite
|
2017-07-03 12:14:23 +02:00
|
|
|
}
|
|
|
|
|
};
|
2016-12-15 01:56:13 +01:00
|
|
|
|
|
|
|
|
Client.Send(request);
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-24 23:43:25 +02:00
|
|
|
internal async Task<bool> AddFriend(ulong steamID) {
|
2019-02-24 17:11:47 +01:00
|
|
|
if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) {
|
2020-08-22 21:41:01 +02:00
|
|
|
throw new ArgumentNullException(nameof(steamID));
|
2018-07-24 23:43:25 +02:00
|
|
|
}
|
|
|
|
|
|
2018-09-27 00:17:52 +02:00
|
|
|
if (!Client.IsConnected) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-24 23:43:25 +02:00
|
|
|
CPlayer_AddFriend_Request request = new CPlayer_AddFriend_Request { steamid = steamID };
|
|
|
|
|
|
|
|
|
|
SteamUnifiedMessages.ServiceMethodResponse response;
|
|
|
|
|
|
|
|
|
|
try {
|
2020-06-15 16:55:57 +02:00
|
|
|
response = await UnifiedPlayerService.SendMessage(x => x.AddFriend(request)).ToLongRunningTask().ConfigureAwait(false);
|
2018-07-24 23:43:25 +02:00
|
|
|
} catch (Exception e) {
|
|
|
|
|
ArchiLogger.LogGenericWarningException(e);
|
2018-12-15 00:27:15 +01:00
|
|
|
|
2018-07-24 23:43:25 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return response.Result == EResult.OK;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-25 17:11:19 +02:00
|
|
|
internal async Task<ulong> GetClanChatGroupID(ulong steamID) {
|
|
|
|
|
if ((steamID == 0) || !new SteamID(steamID).IsClanAccount) {
|
2020-08-22 21:41:01 +02:00
|
|
|
throw new ArgumentNullException(nameof(steamID));
|
2018-07-25 17:11:19 +02:00
|
|
|
}
|
|
|
|
|
|
2018-09-27 00:17:52 +02:00
|
|
|
if (!Client.IsConnected) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-25 17:11:19 +02:00
|
|
|
CClanChatRooms_GetClanChatRoomInfo_Request request = new CClanChatRooms_GetClanChatRoomInfo_Request {
|
|
|
|
|
autocreate = true,
|
|
|
|
|
steamid = steamID
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
SteamUnifiedMessages.ServiceMethodResponse response;
|
|
|
|
|
|
|
|
|
|
try {
|
2020-06-15 16:55:57 +02:00
|
|
|
response = await UnifiedClanChatRoomsService.SendMessage(x => x.GetClanChatRoomInfo(request)).ToLongRunningTask().ConfigureAwait(false);
|
2018-07-25 17:11:19 +02:00
|
|
|
} catch (Exception e) {
|
|
|
|
|
ArchiLogger.LogGenericWarningException(e);
|
2018-12-15 00:27:15 +01:00
|
|
|
|
2018-07-25 17:11:19 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (response.Result != EResult.OK) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CClanChatRooms_GetClanChatRoomInfo_Response body = response.GetDeserializedResponse<CClanChatRooms_GetClanChatRoomInfo_Response>();
|
2018-12-15 00:27:15 +01:00
|
|
|
|
2018-07-25 17:11:19 +02:00
|
|
|
return body.chat_group_summary.chat_group_id;
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-14 21:51:21 +02:00
|
|
|
internal async Task<uint?> GetLevel() {
|
|
|
|
|
if (!Client.IsConnected) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-19 17:16:35 +03:00
|
|
|
CPlayer_GetGameBadgeLevels_Request request = new CPlayer_GetGameBadgeLevels_Request();
|
2018-11-14 21:51:21 +02:00
|
|
|
SteamUnifiedMessages.ServiceMethodResponse response;
|
|
|
|
|
|
|
|
|
|
try {
|
2020-06-15 16:55:57 +02:00
|
|
|
response = await UnifiedPlayerService.SendMessage(x => x.GetGameBadgeLevels(request)).ToLongRunningTask().ConfigureAwait(false);
|
2018-11-14 21:51:21 +02:00
|
|
|
} catch (Exception e) {
|
|
|
|
|
ArchiLogger.LogGenericWarningException(e);
|
2018-12-15 00:27:15 +01:00
|
|
|
|
2018-11-14 21:51:21 +02:00
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (response.Result != EResult.OK) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPlayer_GetGameBadgeLevels_Response body = response.GetDeserializedResponse<CPlayer_GetGameBadgeLevels_Response>();
|
2018-12-15 00:27:15 +01:00
|
|
|
|
2018-11-14 21:51:21 +02:00
|
|
|
return body.player_level;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-22 21:41:01 +02:00
|
|
|
internal async Task<HashSet<ulong>?> GetMyChatGroupIDs() {
|
2018-09-27 00:17:52 +02:00
|
|
|
if (!Client.IsConnected) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-24 23:43:25 +02:00
|
|
|
CChatRoom_GetMyChatRoomGroups_Request request = new CChatRoom_GetMyChatRoomGroups_Request();
|
|
|
|
|
|
|
|
|
|
SteamUnifiedMessages.ServiceMethodResponse response;
|
|
|
|
|
|
|
|
|
|
try {
|
2020-06-15 16:55:57 +02:00
|
|
|
response = await UnifiedChatRoomService.SendMessage(x => x.GetMyChatRoomGroups(request)).ToLongRunningTask().ConfigureAwait(false);
|
2018-07-24 23:43:25 +02:00
|
|
|
} catch (Exception e) {
|
|
|
|
|
ArchiLogger.LogGenericWarningException(e);
|
2018-12-15 00:27:15 +01:00
|
|
|
|
2018-07-24 23:43:25 +02:00
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (response.Result != EResult.OK) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CChatRoom_GetMyChatRoomGroups_Response body = response.GetDeserializedResponse<CChatRoom_GetMyChatRoomGroups_Response>();
|
2018-12-15 00:27:15 +01:00
|
|
|
|
2018-07-24 23:43:25 +02:00
|
|
|
return body.chat_room_groups.Select(chatRoom => chatRoom.group_summary.chat_group_id).ToHashSet();
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-22 21:41:01 +02:00
|
|
|
internal async Task<CPrivacySettings?> GetPrivacySettings() {
|
2020-07-04 23:43:49 +02:00
|
|
|
if (!Client.IsConnected) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPlayer_GetPrivacySettings_Request request = new CPlayer_GetPrivacySettings_Request();
|
|
|
|
|
|
|
|
|
|
SteamUnifiedMessages.ServiceMethodResponse response;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
response = await UnifiedPlayerService.SendMessage(x => x.GetPrivacySettings(request)).ToLongRunningTask().ConfigureAwait(false);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
ArchiLogger.LogGenericWarningException(e);
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (response.Result != EResult.OK) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CPlayer_GetPrivacySettings_Response body = response.GetDeserializedResponse<CPlayer_GetPrivacySettings_Response>();
|
|
|
|
|
|
|
|
|
|
return body.privacy_settings;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-22 21:41:01 +02:00
|
|
|
internal async Task<string?> GetTradeToken() {
|
2018-11-19 17:16:35 +03:00
|
|
|
if (!Client.IsConnected) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CEcon_GetTradeOfferAccessToken_Request request = new CEcon_GetTradeOfferAccessToken_Request();
|
|
|
|
|
|
|
|
|
|
SteamUnifiedMessages.ServiceMethodResponse response;
|
|
|
|
|
|
|
|
|
|
try {
|
2020-06-15 16:55:57 +02:00
|
|
|
response = await UnifiedEconService.SendMessage(x => x.GetTradeOfferAccessToken(request)).ToLongRunningTask().ConfigureAwait(false);
|
2018-11-19 17:16:35 +03:00
|
|
|
} catch (Exception e) {
|
|
|
|
|
ArchiLogger.LogGenericWarningException(e);
|
2018-12-15 00:27:15 +01:00
|
|
|
|
2018-11-19 17:16:35 +03:00
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (response.Result != EResult.OK) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CEcon_GetTradeOfferAccessToken_Response body = response.GetDeserializedResponse<CEcon_GetTradeOfferAccessToken_Response>();
|
2018-12-15 00:27:15 +01:00
|
|
|
|
2018-11-19 17:16:35 +03:00
|
|
|
return body.trade_offer_access_token;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-22 21:41:01 +02:00
|
|
|
internal async Task<string?> GetTwoFactorDeviceIdentifier(ulong steamID) {
|
2020-07-05 00:45:13 +02:00
|
|
|
if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) {
|
2020-08-22 21:41:01 +02:00
|
|
|
throw new ArgumentNullException(nameof(steamID));
|
2020-07-05 00:45:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!Client.IsConnected) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CTwoFactor_Status_Request request = new CTwoFactor_Status_Request {
|
|
|
|
|
steamid = steamID
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
SteamUnifiedMessages.ServiceMethodResponse response;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
response = await UnifiedTwoFactorService.SendMessage(x => x.QueryStatus(request)).ToLongRunningTask().ConfigureAwait(false);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
ArchiLogger.LogGenericWarningException(e);
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (response.Result != EResult.OK) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CTwoFactor_Status_Response body = response.GetDeserializedResponse<CTwoFactor_Status_Response>();
|
|
|
|
|
|
|
|
|
|
return body.device_identifier;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-24 23:43:25 +02:00
|
|
|
internal async Task<bool> JoinChatRoomGroup(ulong chatGroupID) {
|
|
|
|
|
if (chatGroupID == 0) {
|
2020-08-22 21:41:01 +02:00
|
|
|
throw new ArgumentNullException(nameof(chatGroupID));
|
2018-07-24 23:43:25 +02:00
|
|
|
}
|
|
|
|
|
|
2018-09-27 00:17:52 +02:00
|
|
|
if (!Client.IsConnected) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-24 23:43:25 +02:00
|
|
|
CChatRoom_JoinChatRoomGroup_Request request = new CChatRoom_JoinChatRoomGroup_Request { chat_group_id = chatGroupID };
|
|
|
|
|
|
|
|
|
|
SteamUnifiedMessages.ServiceMethodResponse response;
|
|
|
|
|
|
|
|
|
|
try {
|
2020-06-15 16:55:57 +02:00
|
|
|
response = await UnifiedChatRoomService.SendMessage(x => x.JoinChatRoomGroup(request)).ToLongRunningTask().ConfigureAwait(false);
|
2018-07-24 23:43:25 +02:00
|
|
|
} catch (Exception e) {
|
|
|
|
|
ArchiLogger.LogGenericWarningException(e);
|
2018-12-15 00:27:15 +01:00
|
|
|
|
2018-07-24 23:43:25 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return response.Result == EResult.OK;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-22 21:41:01 +02:00
|
|
|
internal async Task PlayGames(IEnumerable<uint> gameIDs, string? gameName = null) {
|
2016-05-30 01:57:06 +02:00
|
|
|
if (gameIDs == null) {
|
2020-08-22 21:41:01 +02:00
|
|
|
throw new ArgumentNullException(nameof(gameIDs));
|
2016-05-30 01:57:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!Client.IsConnected) {
|
2016-01-10 20:55:00 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-15 21:59:12 +02:00
|
|
|
ClientMsgProtobuf<CMsgClientGamesPlayed> request = new ClientMsgProtobuf<CMsgClientGamesPlayed>(EMsg.ClientGamesPlayedWithDataBlob) {
|
|
|
|
|
Body = {
|
|
|
|
|
client_os_type = (uint) Bot.OSType
|
|
|
|
|
}
|
|
|
|
|
};
|
2016-07-12 04:40:56 +02:00
|
|
|
|
2018-03-04 07:29:27 +01:00
|
|
|
byte maxGamesCount = MaxGamesPlayedConcurrently;
|
|
|
|
|
|
2016-07-12 04:40:56 +02:00
|
|
|
if (!string.IsNullOrEmpty(gameName)) {
|
2018-09-08 00:46:40 +02:00
|
|
|
// If we have custom name to display, we must workaround the Steam network broken behaviour and send request on clean non-playing session
|
2017-08-23 17:04:12 +02:00
|
|
|
// This ensures that custom name will in fact display properly
|
|
|
|
|
Client.Send(request);
|
|
|
|
|
await Task.Delay(Bot.CallbackSleep).ConfigureAwait(false);
|
|
|
|
|
|
2018-06-16 07:47:07 +02:00
|
|
|
request.Body.games_played.Add(
|
|
|
|
|
new CMsgClientGamesPlayed.GamePlayed {
|
|
|
|
|
game_extra_info = gameName,
|
|
|
|
|
game_id = new GameID {
|
|
|
|
|
AppType = GameID.GameType.Shortcut,
|
|
|
|
|
ModID = uint.MaxValue
|
|
|
|
|
}
|
2016-12-18 16:03:32 +01:00
|
|
|
}
|
2018-06-16 07:47:07 +02:00
|
|
|
);
|
2018-03-04 07:29:27 +01:00
|
|
|
|
2018-03-06 10:14:48 +01:00
|
|
|
// Max games count is affected by valid AppIDs only, therefore gameName alone doesn't need exclusive slot
|
2018-03-04 07:29:27 +01:00
|
|
|
maxGamesCount++;
|
2016-07-12 04:40:56 +02:00
|
|
|
}
|
|
|
|
|
|
2016-05-13 06:32:42 +02:00
|
|
|
foreach (uint gameID in gameIDs.Where(gameID => gameID != 0)) {
|
Fix latest Steam notifications fuckup
Initially the issue was observed in #697, but that itself wasn't exactly what was fixed here, as multiple evaluation of the same trade is still wanted scenario.
The real issue was reported in http://steamcommunity.com/groups/ascfarm/discussions/1/2425614539578192287/
In a huge TL;DR, Steam is now sending trades notification each time something fetches current trade offers, be it ASF, the user, or some other script.
This will lead to possible ASF trade loop, as we'll get wanted notification about new trades, fetch them, leave some trades untouched, get new notification about trades and so on.
Initially I wanted to fix this in dirty way by just ignoring any extra notifications that happened since API call until 5 extra seconds after we were done with entire parsing, but I found much better solution - Steam actually includes extra info about amount of trades/items in notification (makes sense, since Steam client displays that info too). We can make use of that info and simply ignore any extra notification that results in same or smaller count.
Thanks to that we didn't only add a decent workaround for this recent Steam fuckup, but we also improved internal ASF code that will no longer schedule extra parsing if we accepted/rejected only some of the trades, making me happy with the actual solution.
2017-11-18 17:20:24 +01:00
|
|
|
request.Body.games_played.Add(new CMsgClientGamesPlayed.GamePlayed { game_id = new GameID(gameID) });
|
2018-01-21 23:19:30 +01:00
|
|
|
|
2018-03-04 07:29:27 +01:00
|
|
|
if (request.Body.games_played.Count >= maxGamesCount) {
|
2018-01-21 23:19:30 +01:00
|
|
|
break;
|
|
|
|
|
}
|
2015-10-25 06:16:50 +01:00
|
|
|
}
|
2016-01-02 17:22:35 +01:00
|
|
|
|
2015-10-25 06:16:50 +01:00
|
|
|
Client.Send(request);
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-22 21:41:01 +02:00
|
|
|
internal async Task<RedeemGuestPassResponseCallback?> RedeemGuestPass(ulong guestPassID) {
|
2016-09-25 01:23:31 +02:00
|
|
|
if (guestPassID == 0) {
|
2020-08-22 21:41:01 +02:00
|
|
|
throw new ArgumentNullException(nameof(guestPassID));
|
2016-09-25 01:23:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!Client.IsConnected) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-24 07:46:37 +01:00
|
|
|
ClientMsgProtobuf<CMsgClientRedeemGuestPass> request = new ClientMsgProtobuf<CMsgClientRedeemGuestPass>(EMsg.ClientRedeemGuestPass) {
|
2017-07-03 12:14:23 +02:00
|
|
|
SourceJobID = Client.GetNextJobID(),
|
|
|
|
|
Body = { guest_pass_id = guestPassID }
|
2016-11-24 07:46:37 +01:00
|
|
|
};
|
2016-09-25 01:23:31 +02:00
|
|
|
|
|
|
|
|
Client.Send(request);
|
|
|
|
|
|
|
|
|
|
try {
|
2020-06-15 16:55:57 +02:00
|
|
|
return await new AsyncJob<RedeemGuestPassResponseCallback>(Client, request.SourceJobID).ToLongRunningTask().ConfigureAwait(false);
|
2016-09-25 01:23:31 +02:00
|
|
|
} catch (Exception e) {
|
2016-11-06 12:06:02 +01:00
|
|
|
ArchiLogger.LogGenericException(e);
|
2018-12-15 00:27:15 +01:00
|
|
|
|
2016-09-25 01:23:31 +02:00
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-22 21:41:01 +02:00
|
|
|
internal async Task<PurchaseResponseCallback?> RedeemKey(string key) {
|
2016-05-30 01:57:06 +02:00
|
|
|
if (string.IsNullOrEmpty(key)) {
|
2020-08-22 21:41:01 +02:00
|
|
|
throw new ArgumentNullException(nameof(key));
|
2016-05-30 01:57:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!Client.IsConnected) {
|
2016-01-02 17:22:35 +01:00
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-24 07:46:37 +01:00
|
|
|
ClientMsgProtobuf<CMsgClientRegisterKey> request = new ClientMsgProtobuf<CMsgClientRegisterKey>(EMsg.ClientRegisterKey) {
|
2017-07-03 12:14:23 +02:00
|
|
|
SourceJobID = Client.GetNextJobID(),
|
|
|
|
|
Body = { key = key }
|
2016-11-24 07:46:37 +01:00
|
|
|
};
|
2016-02-24 06:20:35 +01:00
|
|
|
|
2015-10-25 06:16:50 +01:00
|
|
|
Client.Send(request);
|
2016-04-02 19:23:09 +02:00
|
|
|
|
2016-03-06 21:49:34 +01:00
|
|
|
try {
|
2020-06-15 16:55:57 +02:00
|
|
|
return await new AsyncJob<PurchaseResponseCallback>(Client, request.SourceJobID).ToLongRunningTask().ConfigureAwait(false);
|
2016-03-06 21:49:34 +01:00
|
|
|
} catch (Exception e) {
|
2016-11-06 12:06:02 +01:00
|
|
|
ArchiLogger.LogGenericException(e);
|
2018-12-15 00:27:15 +01:00
|
|
|
|
2016-03-06 21:49:34 +01:00
|
|
|
return null;
|
|
|
|
|
}
|
2015-10-25 06:16:50 +01:00
|
|
|
}
|
|
|
|
|
|
2018-07-24 23:43:25 +02:00
|
|
|
internal async Task<bool> RemoveFriend(ulong steamID) {
|
2019-02-24 17:11:47 +01:00
|
|
|
if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) {
|
2020-08-22 21:41:01 +02:00
|
|
|
throw new ArgumentNullException(nameof(steamID));
|
2018-07-24 23:43:25 +02:00
|
|
|
}
|
|
|
|
|
|
2018-09-27 00:17:52 +02:00
|
|
|
if (!Client.IsConnected) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-24 23:43:25 +02:00
|
|
|
CPlayer_RemoveFriend_Request request = new CPlayer_RemoveFriend_Request { steamid = steamID };
|
|
|
|
|
|
|
|
|
|
SteamUnifiedMessages.ServiceMethodResponse response;
|
|
|
|
|
|
|
|
|
|
try {
|
2020-06-15 16:55:57 +02:00
|
|
|
response = await UnifiedPlayerService.SendMessage(x => x.RemoveFriend(request)).ToLongRunningTask().ConfigureAwait(false);
|
2018-07-24 23:43:25 +02:00
|
|
|
} catch (Exception e) {
|
|
|
|
|
ArchiLogger.LogGenericWarningException(e);
|
2018-12-15 00:27:15 +01:00
|
|
|
|
2018-07-24 23:43:25 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return response.Result == EResult.OK;
|
|
|
|
|
}
|
|
|
|
|
|
Fix latest Steam notifications fuckup
Initially the issue was observed in #697, but that itself wasn't exactly what was fixed here, as multiple evaluation of the same trade is still wanted scenario.
The real issue was reported in http://steamcommunity.com/groups/ascfarm/discussions/1/2425614539578192287/
In a huge TL;DR, Steam is now sending trades notification each time something fetches current trade offers, be it ASF, the user, or some other script.
This will lead to possible ASF trade loop, as we'll get wanted notification about new trades, fetch them, leave some trades untouched, get new notification about trades and so on.
Initially I wanted to fix this in dirty way by just ignoring any extra notifications that happened since API call until 5 extra seconds after we were done with entire parsing, but I found much better solution - Steam actually includes extra info about amount of trades/items in notification (makes sense, since Steam client displays that info too). We can make use of that info and simply ignore any extra notification that results in same or smaller count.
Thanks to that we didn't only add a decent workaround for this recent Steam fuckup, but we also improved internal ASF code that will no longer schedule extra parsing if we accepted/rejected only some of the trades, making me happy with the actual solution.
2017-11-18 17:20:24 +01:00
|
|
|
internal void RequestItemAnnouncements() {
|
2018-09-27 00:17:52 +02:00
|
|
|
if (!Client.IsConnected) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
Fix latest Steam notifications fuckup
Initially the issue was observed in #697, but that itself wasn't exactly what was fixed here, as multiple evaluation of the same trade is still wanted scenario.
The real issue was reported in http://steamcommunity.com/groups/ascfarm/discussions/1/2425614539578192287/
In a huge TL;DR, Steam is now sending trades notification each time something fetches current trade offers, be it ASF, the user, or some other script.
This will lead to possible ASF trade loop, as we'll get wanted notification about new trades, fetch them, leave some trades untouched, get new notification about trades and so on.
Initially I wanted to fix this in dirty way by just ignoring any extra notifications that happened since API call until 5 extra seconds after we were done with entire parsing, but I found much better solution - Steam actually includes extra info about amount of trades/items in notification (makes sense, since Steam client displays that info too). We can make use of that info and simply ignore any extra notification that results in same or smaller count.
Thanks to that we didn't only add a decent workaround for this recent Steam fuckup, but we also improved internal ASF code that will no longer schedule extra parsing if we accepted/rejected only some of the trades, making me happy with the actual solution.
2017-11-18 17:20:24 +01:00
|
|
|
ClientMsgProtobuf<CMsgClientRequestItemAnnouncements> request = new ClientMsgProtobuf<CMsgClientRequestItemAnnouncements>(EMsg.ClientRequestItemAnnouncements);
|
|
|
|
|
Client.Send(request);
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-27 00:09:00 +02:00
|
|
|
internal async Task<EResult> SendMessage(ulong steamID, string message) {
|
2019-02-24 17:11:47 +01:00
|
|
|
if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || string.IsNullOrEmpty(message)) {
|
2020-08-22 21:41:01 +02:00
|
|
|
throw new ArgumentNullException(nameof(steamID) + " || " + nameof(message));
|
2020-04-18 16:58:30 +02:00
|
|
|
}
|
|
|
|
|
|
2018-09-27 00:17:52 +02:00
|
|
|
if (!Client.IsConnected) {
|
|
|
|
|
return EResult.NoConnection;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-24 23:43:25 +02:00
|
|
|
CFriendMessages_SendMessage_Request request = new CFriendMessages_SendMessage_Request {
|
|
|
|
|
chat_entry_type = (int) EChatEntryType.ChatMsg,
|
2018-07-25 01:01:15 +02:00
|
|
|
contains_bbcode = true,
|
2018-07-24 23:43:25 +02:00
|
|
|
message = message,
|
|
|
|
|
steamid = steamID
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
SteamUnifiedMessages.ServiceMethodResponse response;
|
|
|
|
|
|
|
|
|
|
try {
|
2020-06-15 16:55:57 +02:00
|
|
|
response = await UnifiedFriendMessagesService.SendMessage(x => x.SendMessage(request)).ToLongRunningTask().ConfigureAwait(false);
|
2018-07-24 23:43:25 +02:00
|
|
|
} catch (Exception e) {
|
|
|
|
|
ArchiLogger.LogGenericWarningException(e);
|
2018-12-15 00:27:15 +01:00
|
|
|
|
2018-09-27 00:09:00 +02:00
|
|
|
return EResult.Timeout;
|
2018-07-24 23:43:25 +02:00
|
|
|
}
|
|
|
|
|
|
2018-09-27 00:09:00 +02:00
|
|
|
return response.Result;
|
2018-07-24 23:43:25 +02:00
|
|
|
}
|
|
|
|
|
|
2018-09-27 00:09:00 +02:00
|
|
|
internal async Task<EResult> SendMessage(ulong chatGroupID, ulong chatID, string message) {
|
2018-07-24 23:43:25 +02:00
|
|
|
if ((chatGroupID == 0) || (chatID == 0) || string.IsNullOrEmpty(message)) {
|
2020-08-22 21:41:01 +02:00
|
|
|
throw new ArgumentNullException(nameof(chatGroupID) + " || " + nameof(chatID) + " || " + nameof(message));
|
2020-04-18 16:58:30 +02:00
|
|
|
}
|
|
|
|
|
|
2018-09-27 00:17:52 +02:00
|
|
|
if (!Client.IsConnected) {
|
|
|
|
|
return EResult.NoConnection;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-24 23:43:25 +02:00
|
|
|
CChatRoom_SendChatMessage_Request request = new CChatRoom_SendChatMessage_Request {
|
|
|
|
|
chat_group_id = chatGroupID,
|
|
|
|
|
chat_id = chatID,
|
|
|
|
|
message = message
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
SteamUnifiedMessages.ServiceMethodResponse response;
|
|
|
|
|
|
|
|
|
|
try {
|
2020-06-15 16:55:57 +02:00
|
|
|
response = await UnifiedChatRoomService.SendMessage(x => x.SendChatMessage(request)).ToLongRunningTask().ConfigureAwait(false);
|
2018-07-24 23:43:25 +02:00
|
|
|
} catch (Exception e) {
|
|
|
|
|
ArchiLogger.LogGenericWarningException(e);
|
2018-12-15 00:27:15 +01:00
|
|
|
|
2018-09-27 00:09:00 +02:00
|
|
|
return EResult.Timeout;
|
2018-07-24 23:43:25 +02:00
|
|
|
}
|
|
|
|
|
|
2019-02-24 17:11:47 +01:00
|
|
|
return response.Result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal async Task<EResult> SendTypingStatus(ulong steamID) {
|
|
|
|
|
if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) {
|
2020-08-22 21:41:01 +02:00
|
|
|
throw new ArgumentNullException(nameof(steamID));
|
2020-04-18 16:58:30 +02:00
|
|
|
}
|
|
|
|
|
|
2019-02-24 17:11:47 +01:00
|
|
|
if (!Client.IsConnected) {
|
|
|
|
|
return EResult.NoConnection;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CFriendMessages_SendMessage_Request request = new CFriendMessages_SendMessage_Request {
|
|
|
|
|
chat_entry_type = (int) EChatEntryType.Typing,
|
|
|
|
|
steamid = steamID
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
SteamUnifiedMessages.ServiceMethodResponse response;
|
|
|
|
|
|
|
|
|
|
try {
|
2020-06-15 16:55:57 +02:00
|
|
|
response = await UnifiedFriendMessagesService.SendMessage(x => x.SendMessage(request)).ToLongRunningTask().ConfigureAwait(false);
|
2019-02-24 17:11:47 +01:00
|
|
|
} catch (Exception e) {
|
|
|
|
|
ArchiLogger.LogGenericWarningException(e);
|
|
|
|
|
|
|
|
|
|
return EResult.Timeout;
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-27 00:09:00 +02:00
|
|
|
return response.Result;
|
2018-07-24 23:43:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal void SetCurrentMode(uint chatMode) {
|
|
|
|
|
if (chatMode == 0) {
|
2020-08-22 21:41:01 +02:00
|
|
|
throw new ArgumentNullException(nameof(chatMode));
|
2015-12-20 19:01:05 +01:00
|
|
|
}
|
|
|
|
|
|
2018-09-27 00:17:52 +02:00
|
|
|
if (!Client.IsConnected) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-24 23:43:25 +02:00
|
|
|
ClientMsgProtobuf<CMsgClientUIMode> request = new ClientMsgProtobuf<CMsgClientUIMode>(EMsg.ClientCurrentUIMode) { Body = { chat_mode = chatMode } };
|
|
|
|
|
Client.Send(request);
|
2015-12-20 19:01:05 +01:00
|
|
|
}
|
|
|
|
|
|
2019-10-13 17:21:40 +02:00
|
|
|
[SuppressMessage("ReSharper", "MemberCanBeInternal")]
|
2018-10-29 21:43:44 +01:00
|
|
|
public sealed class PurchaseResponseCallback : CallbackMsg {
|
2020-08-22 21:41:01 +02:00
|
|
|
public readonly Dictionary<uint, string>? Items;
|
2016-11-24 07:32:16 +01:00
|
|
|
|
2018-10-29 21:43:44 +01:00
|
|
|
public EPurchaseResultDetail PurchaseResultDetail { get; internal set; }
|
|
|
|
|
public EResult Result { get; internal set; }
|
2016-11-24 07:32:16 +01:00
|
|
|
|
2019-10-13 13:10:07 +02:00
|
|
|
internal PurchaseResponseCallback(EResult result, EPurchaseResultDetail purchaseResult) {
|
|
|
|
|
if (!Enum.IsDefined(typeof(EResult), result) || !Enum.IsDefined(typeof(EPurchaseResultDetail), purchaseResult)) {
|
|
|
|
|
throw new ArgumentNullException(nameof(result) + " || " + nameof(purchaseResult));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Result = result;
|
|
|
|
|
PurchaseResultDetail = purchaseResult;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-22 21:41:01 +02:00
|
|
|
internal PurchaseResponseCallback(JobID jobID, CMsgClientPurchaseResponse msg) {
|
2016-11-24 07:32:16 +01:00
|
|
|
if ((jobID == null) || (msg == null)) {
|
|
|
|
|
throw new ArgumentNullException(nameof(jobID) + " || " + nameof(msg));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
JobID = jobID;
|
2017-02-11 22:24:49 +01:00
|
|
|
PurchaseResultDetail = (EPurchaseResultDetail) msg.purchase_result_details;
|
2017-04-05 15:03:27 +02:00
|
|
|
Result = (EResult) msg.eresult;
|
2016-11-24 07:32:16 +01:00
|
|
|
|
|
|
|
|
if (msg.purchase_receipt_info == null) {
|
2017-01-31 01:10:01 +01:00
|
|
|
ASF.ArchiLogger.LogNullError(nameof(msg.purchase_receipt_info));
|
2018-12-15 00:27:15 +01:00
|
|
|
|
2016-11-24 07:32:16 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
KeyValue receiptInfo = new KeyValue();
|
2018-12-15 00:27:15 +01:00
|
|
|
|
2016-11-24 07:32:16 +01:00
|
|
|
using (MemoryStream ms = new MemoryStream(msg.purchase_receipt_info)) {
|
|
|
|
|
if (!receiptInfo.TryReadAsBinary(ms)) {
|
2017-01-31 01:10:01 +01:00
|
|
|
ASF.ArchiLogger.LogNullError(nameof(ms));
|
2018-12-15 00:27:15 +01:00
|
|
|
|
2016-11-24 07:32:16 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
List<KeyValue> lineItems = receiptInfo["lineitems"].Children;
|
2018-12-15 00:27:15 +01:00
|
|
|
|
2016-11-24 07:32:16 +01:00
|
|
|
if (lineItems.Count == 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Items = new Dictionary<uint, string>(lineItems.Count);
|
2018-12-15 00:27:15 +01:00
|
|
|
|
2016-11-24 07:32:16 +01:00
|
|
|
foreach (KeyValue lineItem in lineItems) {
|
|
|
|
|
uint packageID = lineItem["PackageID"].AsUnsignedInteger();
|
2018-12-15 00:27:15 +01:00
|
|
|
|
2016-11-24 07:32:16 +01:00
|
|
|
if (packageID == 0) {
|
2017-01-27 21:57:23 +01:00
|
|
|
// Coupons have PackageID of -1 (don't ask me why)
|
|
|
|
|
// We'll use ItemAppID in this case
|
2016-11-24 07:32:16 +01:00
|
|
|
packageID = lineItem["ItemAppID"].AsUnsignedInteger();
|
2018-12-15 00:27:15 +01:00
|
|
|
|
2016-11-24 07:32:16 +01:00
|
|
|
if (packageID == 0) {
|
2017-01-31 01:10:01 +01:00
|
|
|
ASF.ArchiLogger.LogNullError(nameof(packageID));
|
2018-12-15 00:27:15 +01:00
|
|
|
|
2016-11-24 07:32:16 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-22 21:41:01 +02:00
|
|
|
string? gameName = lineItem["ItemDescription"].AsString();
|
2018-12-15 00:27:15 +01:00
|
|
|
|
2016-11-24 07:32:16 +01:00
|
|
|
if (string.IsNullOrEmpty(gameName)) {
|
2017-01-31 01:10:01 +01:00
|
|
|
ASF.ArchiLogger.LogNullError(nameof(gameName));
|
2018-12-15 00:27:15 +01:00
|
|
|
|
2016-11-24 07:32:16 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-27 21:57:23 +01:00
|
|
|
// Apparently steam expects client to decode sent HTML
|
|
|
|
|
gameName = WebUtility.HtmlDecode(gameName);
|
|
|
|
|
Items[packageID] = gameName;
|
2016-11-24 07:32:16 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-14 21:37:26 +01:00
|
|
|
public sealed class UserNotificationsCallback : CallbackMsg {
|
2017-12-01 01:36:58 +01:00
|
|
|
internal readonly Dictionary<EUserNotification, uint> Notifications;
|
|
|
|
|
|
2020-08-22 21:41:01 +02:00
|
|
|
internal UserNotificationsCallback(JobID jobID, CMsgClientUserNotifications msg) {
|
2017-12-01 01:36:58 +01:00
|
|
|
if ((jobID == null) || (msg == null)) {
|
|
|
|
|
throw new ArgumentNullException(nameof(jobID) + " || " + nameof(msg));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
JobID = jobID;
|
|
|
|
|
|
2019-11-14 21:40:40 +01:00
|
|
|
// We might get null body here, and that means there are no notifications related to trading
|
|
|
|
|
// TODO: Check if this workaround is still needed
|
|
|
|
|
Notifications = new Dictionary<EUserNotification, uint> { { EUserNotification.Trading, 0 } };
|
2019-11-14 21:37:26 +01:00
|
|
|
|
2019-11-14 21:40:40 +01:00
|
|
|
if (msg.notifications == null) {
|
2017-12-01 01:36:58 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach (CMsgClientUserNotifications.Notification notification in msg.notifications) {
|
|
|
|
|
EUserNotification type = (EUserNotification) notification.user_notification_type;
|
|
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
|
case EUserNotification.AccountAlerts:
|
|
|
|
|
case EUserNotification.Chat:
|
|
|
|
|
case EUserNotification.Comments:
|
|
|
|
|
case EUserNotification.GameTurns:
|
|
|
|
|
case EUserNotification.Gifts:
|
|
|
|
|
case EUserNotification.HelpRequestReplies:
|
|
|
|
|
case EUserNotification.Invites:
|
|
|
|
|
case EUserNotification.Items:
|
|
|
|
|
case EUserNotification.ModeratorMessages:
|
|
|
|
|
case EUserNotification.Trading:
|
|
|
|
|
break;
|
|
|
|
|
default:
|
2017-12-15 10:37:51 +01:00
|
|
|
ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningUnknownValuePleaseReport, nameof(type), type));
|
2018-12-15 00:27:15 +01:00
|
|
|
|
2019-11-14 21:37:26 +01:00
|
|
|
break;
|
2017-12-01 01:36:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Notifications[type] = notification.count;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-22 21:41:01 +02:00
|
|
|
internal UserNotificationsCallback(JobID jobID, CMsgClientItemAnnouncements msg) {
|
2017-12-01 01:36:58 +01:00
|
|
|
if ((jobID == null) || (msg == null)) {
|
|
|
|
|
throw new ArgumentNullException(nameof(jobID) + " || " + nameof(msg));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
JobID = jobID;
|
|
|
|
|
Notifications = new Dictionary<EUserNotification, uint>(1) { { EUserNotification.Items, msg.count_new_items } };
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-22 21:41:01 +02:00
|
|
|
internal UserNotificationsCallback(JobID jobID, CMsgClientCommentNotifications msg) {
|
2020-04-06 20:37:53 +02:00
|
|
|
if ((jobID == null) || (msg == null)) {
|
|
|
|
|
throw new ArgumentNullException(nameof(jobID) + " || " + nameof(msg));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
JobID = jobID;
|
|
|
|
|
Notifications = new Dictionary<EUserNotification, uint>(1) { { EUserNotification.Comments, msg.count_new_comments + msg.count_new_comments_owner + msg.count_new_comments_subscriptions } };
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-10 22:33:07 +01:00
|
|
|
[PublicAPI]
|
2019-11-14 21:37:26 +01:00
|
|
|
public enum EUserNotification : byte {
|
2017-12-01 01:36:58 +01:00
|
|
|
Unknown,
|
2020-08-22 21:41:01 +02:00
|
|
|
Trading = 1,
|
|
|
|
|
GameTurns = 2,
|
|
|
|
|
ModeratorMessages = 3,
|
|
|
|
|
Comments = 4,
|
|
|
|
|
Items = 5,
|
|
|
|
|
Invites = 6,
|
|
|
|
|
Gifts = 8,
|
|
|
|
|
Chat = 9,
|
|
|
|
|
HelpRequestReplies = 10,
|
|
|
|
|
AccountAlerts = 11
|
2017-12-01 01:36:58 +01:00
|
|
|
}
|
|
|
|
|
}
|
2018-06-11 21:40:52 +02:00
|
|
|
|
2019-11-14 21:37:26 +01:00
|
|
|
internal sealed class PlayingSessionStateCallback : CallbackMsg {
|
|
|
|
|
internal readonly bool PlayingBlocked;
|
|
|
|
|
|
2020-08-22 21:41:01 +02:00
|
|
|
internal PlayingSessionStateCallback(JobID jobID, CMsgClientPlayingSessionState msg) {
|
2019-11-14 21:37:26 +01:00
|
|
|
if ((jobID == null) || (msg == null)) {
|
|
|
|
|
throw new ArgumentNullException(nameof(jobID) + " || " + nameof(msg));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
JobID = jobID;
|
|
|
|
|
PlayingBlocked = msg.playing_blocked;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal sealed class RedeemGuestPassResponseCallback : CallbackMsg {
|
|
|
|
|
internal readonly EResult Result;
|
|
|
|
|
|
2020-08-22 21:41:01 +02:00
|
|
|
internal RedeemGuestPassResponseCallback(JobID jobID, CMsgClientRedeemGuestPassResponse msg) {
|
2019-11-14 21:37:26 +01:00
|
|
|
if ((jobID == null) || (msg == null)) {
|
|
|
|
|
throw new ArgumentNullException(nameof(jobID) + " || " + nameof(msg));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
JobID = jobID;
|
|
|
|
|
Result = (EResult) msg.eresult;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal sealed class SharedLibraryLockStatusCallback : CallbackMsg {
|
|
|
|
|
internal readonly ulong LibraryLockedBySteamID;
|
|
|
|
|
|
2020-08-22 21:41:01 +02:00
|
|
|
internal SharedLibraryLockStatusCallback(JobID jobID, CMsgClientSharedLibraryLockStatus msg) {
|
2019-11-14 21:37:26 +01:00
|
|
|
if ((jobID == null) || (msg == null)) {
|
|
|
|
|
throw new ArgumentNullException(nameof(jobID) + " || " + nameof(msg));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
JobID = jobID;
|
|
|
|
|
|
|
|
|
|
if (msg.own_library_locked_by == 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LibraryLockedBySteamID = new SteamID(msg.own_library_locked_by, EUniverse.Public, EAccountType.Individual);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-11 21:40:52 +02:00
|
|
|
internal sealed class VanityURLChangedCallback : CallbackMsg {
|
|
|
|
|
internal readonly string VanityURL;
|
|
|
|
|
|
2020-08-22 21:41:01 +02:00
|
|
|
internal VanityURLChangedCallback(JobID jobID, CMsgClientVanityURLChangedNotification msg) {
|
2018-06-11 21:40:52 +02:00
|
|
|
if ((jobID == null) || (msg == null)) {
|
|
|
|
|
throw new ArgumentNullException(nameof(jobID) + " || " + nameof(msg));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
JobID = jobID;
|
|
|
|
|
VanityURL = msg.vanity_url;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-07-04 23:43:49 +02:00
|
|
|
|
|
|
|
|
internal enum EPrivacySetting : byte {
|
|
|
|
|
Unknown,
|
|
|
|
|
Private,
|
|
|
|
|
FriendsOnly,
|
|
|
|
|
Public
|
|
|
|
|
}
|
2015-10-25 06:16:50 +01:00
|
|
|
}
|
2018-07-11 16:31:46 +02:00
|
|
|
}
|