Beispiel #1
0
        public void InitializesInstancesCorrectly()
        {
            SteamID sid = new SteamID();

            sid.SetFromSteam3String("[g:1:1234]");
            Assert.Equal(EUniverse.Public, sid.AccountUniverse);
            Assert.Equal(EAccountType.Clan, sid.AccountType);
            Assert.Equal(0u, sid.AccountInstance);
            Assert.Equal(1234u, sid.AccountID);

            sid.SetFromSteam3String("[T:1:1234]");
            Assert.Equal(EUniverse.Public, sid.AccountUniverse);
            Assert.Equal(EAccountType.Chat, sid.AccountType);
            Assert.Equal(0u, sid.AccountInstance);
            Assert.Equal(1234u, sid.AccountID);

            sid.SetFromSteam3String("[c:1:1234]");
            Assert.Equal(EUniverse.Public, sid.AccountUniverse);
            Assert.Equal(EAccountType.Chat, sid.AccountType);
            Assert.Equal((uint)SteamID.ChatInstanceFlags.Clan, sid.AccountInstance);
            Assert.Equal(1234u, sid.AccountID);

            sid.SetFromSteam3String("[L:1:1234]");
            Assert.Equal(EUniverse.Public, sid.AccountUniverse);
            Assert.Equal(EAccountType.Chat, sid.AccountType);
            Assert.Equal(( uint )SteamID.ChatInstanceFlags.Lobby, sid.AccountInstance);
            Assert.Equal(1234u, sid.AccountID);
        }
Beispiel #2
0
        // Renders from a "[U:1:263966176]" form.
        public static SteamID FromSteamID3(string steamID3)
        {
            var id = new SteamID();

            id.SetFromSteam3String(steamID3);
            return(id);
        }
Beispiel #3
0
        public void SetFromSteam3StringCorrectParse()
        {
            SteamID sidUser = new SteamID();

            sidUser.SetFromSteam3String("[U:1:123]");
            Assert.Equal(123u, sidUser.AccountID);
            Assert.Equal(EUniverse.Public, sidUser.AccountUniverse);
            Assert.Equal(1u, sidUser.AccountInstance);
            Assert.Equal(EAccountType.Individual, sidUser.AccountType);

            SteamID sidAnonGSUser = new SteamID();

            sidAnonGSUser.SetFromSteam3String("[A:1:123:456]");
            Assert.Equal(123u, sidAnonGSUser.AccountID);
            Assert.Equal(EUniverse.Public, sidAnonGSUser.AccountUniverse);
            Assert.Equal(456u, sidAnonGSUser.AccountInstance);
            Assert.Equal(EAccountType.AnonGameServer, sidAnonGSUser.AccountType);

            SteamID sidLobby = new SteamID();

            sidLobby.SetFromSteam3String("[L:1:123]");
            Assert.Equal(123u, sidLobby.AccountID);
            Assert.Equal(EUniverse.Public, sidLobby.AccountUniverse);
            Assert.True(((SteamID.ChatInstanceFlags)sidLobby.AccountInstance).HasFlag(SteamID.ChatInstanceFlags.Lobby));
            Assert.Equal(EAccountType.Chat, sidLobby.AccountType);

            SteamID sidClanChat = new SteamID();

            sidClanChat.SetFromSteam3String("[c:1:123]");
            Assert.Equal(123u, sidClanChat.AccountID);
            Assert.Equal(EUniverse.Public, sidClanChat.AccountUniverse);
            Assert.True(((SteamID.ChatInstanceFlags)sidClanChat.AccountInstance).HasFlag(SteamID.ChatInstanceFlags.Clan));
            Assert.Equal(EAccountType.Chat, sidClanChat.AccountType);
        }
        private static async Task <bool> UpdateUserSteamFrinedsAsync([NotNull] string userId, KeylolDbContext dbContext,
                                                                     KeylolUserManager userManager, RedisProvider redis)
        {
            var cacheKey    = UserSteamFriendRecordsCrawlerStampCacheKey(userId);
            var redisDb     = redis.GetDatabase();
            var cacheResult = await redisDb.StringGetAsync(cacheKey);

            if (cacheResult.HasValue)
            {
                return(false);
            }
            await redisDb.StringSetAsync(cacheKey, DateTime.Now.ToTimestamp(), UserSteamFriendsUpdatePeriod);

            try
            {
                var steamId = new SteamID();
                steamId.SetFromSteam3String(await userManager.GetSteamIdAsync(userId));
                var result = JObject.Parse(await HttpClient.GetStringAsync(
                                               $"http://api.steampowered.com/ISteamUser/GetFriendList/v1/?key={ApiKey}&format=json&steamid={steamId.ConvertToUInt64()}&relationship=friend"));
                var oldRecords = (await dbContext.UserSteamFriendRecords.Where(r => r.UserId == userId).ToListAsync())
                                 .ToDictionary(r => r.FriendSteamId, r => r);
                foreach (var friend in result["friendslist"]["friends"])
                {
                    var friendSteamId = (new SteamID(ulong.Parse((string)friend["steamid"]))).Render(true);
                    UserSteamFriendRecord record;
                    if (oldRecords.TryGetValue(friendSteamId, out record))
                    {
                        oldRecords.Remove(friendSteamId);
                    }
                    else
                    {
                        record = new UserSteamFriendRecord
                        {
                            UserId        = userId,
                            FriendSteamId = friendSteamId
                        };
                        dbContext.UserSteamFriendRecords.Add(record);
                        if (friend["friend_since"] != null)
                        {
                            record.FriendSince = Helpers.DateTimeFromTimeStamp((ulong)friend["friend_since"]);
                        }
                    }
                }
                dbContext.UserSteamFriendRecords.RemoveRange(oldRecords.Values);
                await dbContext.SaveChangesAsync();

                return(true);
            }
            catch (Exception)
            {
                await redisDb.KeyExpireAsync(cacheKey, SilenceTime);

                return(false);
            }
        }
Beispiel #5
0
        public void AddFriend(string botId, string steamId)
        {
            var botInstance = SteamBot.BotInstances?.SingleOrDefault(b => b.Id == botId);

            if (botInstance == null)
            {
                return;
            }
            var id = new SteamID();

            id.SetFromSteam3String(steamId);
            botInstance.SteamFriends.AddFriend(id);
        }
Beispiel #6
0
        public string GetUserProfileName(string botId, string steamId)
        {
            var botInstance = SteamBot.BotInstances?.SingleOrDefault(b => b.Id == botId);

            if (botInstance == null)
            {
                return(null);
            }
            var id = new SteamID();

            id.SetFromSteam3String(steamId);
            return(botInstance.SteamFriends.GetFriendPersonaName(id));
        }
Beispiel #7
0
        public string GetUserAvatarHash(string botId, string steamId)
        {
            var botInstance = SteamBot.BotInstances?.SingleOrDefault(b => b.Id == botId);

            if (botInstance == null)
            {
                return(null);
            }
            var id = new SteamID();

            id.SetFromSteam3String(steamId);
            return(BitConverter.ToString(botInstance.SteamFriends.GetFriendAvatar(id))
                   .Replace("-", string.Empty).ToLower());
        }
Beispiel #8
0
        public void SetFromSteam3StringHandlesInvalid()
        {
            SteamID sid = new SteamID();

            bool setFromNullString = sid.SetFromSteam3String(null);

            Assert.False(setFromNullString);

            bool setFromEmptyString = sid.SetFromSteam3String("");

            Assert.False(setFromEmptyString);

            bool setFromInvalidString = sid.SetFromSteam3String("NOT A STEAMID!");

            Assert.False(setFromInvalidString);

            bool setFromInvalidAccountId = sid.SetFromSteam3String("STEAM_0:1:999999999999999999999999999999");

            Assert.False(setFromInvalidAccountId);

            bool setFromSteam2String = sid.SetFromSteam3String("STEAM_0:1:4491990");

            Assert.False(setFromSteam2String);
        }
Beispiel #9
0
        public async Task LinkAsync(string idArg)
        {
            SocketUser sender  = Context.User;
            UserData   user    = new UserData(sender.Id);
            SteamID    steamId = new SteamID();

            // Checks steamid input to determine what type of id it is
            if (idArg.Contains("STEAM_"))
            {
                steamId.SetFromString(idArg, EUniverse.Public);
            }
            else if (ulong.TryParse(idArg, out ulong id))
            {
                steamId.SetFromUInt64(id);
            }
            else if (Regex.Match(idArg, @"^\[{1}\b[A-Z]\:{1}[0-9]\b").Success)
            {
                steamId.SetFromSteam3String(idArg);
            }
            else
            {
                await Error();
            }

            // Stores the steamid to an int64
            user.SteamId = steamId.ConvertToUInt64();

            // Creates an user. Returns true if an error accures
            // Duplicates do not need to be checked both discord and steam ids are unique
            bool errored = await user.Create();

            var persona = await Program.SteamWebManager.GetUserName(user.SteamId);

            Console.WriteLine($"{persona}");

            // If errored bot responds the the error messaged set in user or confirms linked accounts
            // These are made to be more user friendly
            if (errored)
            {
                await ReplyAsync(user.err);
            }
            else
            {
                await ReplyAsync($"Account Linked! {sender.Mention} -> {persona}");
            }
        }
Beispiel #10
0
        public void SetFromOldStyleSteam3StringCorrectParse()
        {
            SteamID sidMultiseat = new SteamID();

            sidMultiseat.SetFromSteam3String("[M:1:123(456)]");
            Assert.Equal(123u, sidMultiseat.AccountID);
            Assert.Equal(EUniverse.Public, sidMultiseat.AccountUniverse);
            Assert.Equal(456u, sidMultiseat.AccountInstance);
            Assert.Equal(EAccountType.Multiseat, sidMultiseat.AccountType);

            SteamID sidAnonGSUser = new SteamID();

            sidAnonGSUser.SetFromSteam3String("[A:1:123(456)]");
            Assert.Equal(123u, sidAnonGSUser.AccountID);
            Assert.Equal(EUniverse.Public, sidAnonGSUser.AccountUniverse);
            Assert.Equal(456u, sidAnonGSUser.AccountInstance);
            Assert.Equal(EAccountType.AnonGameServer, sidAnonGSUser.AccountType);
        }
Beispiel #11
0
        public void Steam3StringSymmetric()
        {
            var steamIds = new[]
            {
                "[U:1:123]",
                "[U:1:123:2]",
                "[G:1:626]",
                "[A:2:165:1234]",
            };

            foreach (var steamId in steamIds)
            {
                SteamID sid    = new SteamID();
                bool    parsed = sid.SetFromSteam3String(steamId);
                Assert.True(parsed);
                Assert.Equal(steamId, sid.Render(steam3: true));
            }
        }
Beispiel #12
0
        public void SendChatMessage(string botId, string steamId, string message, bool logMessage)
        {
            var botInstance = SteamBot.BotInstances?.SingleOrDefault(b => b.Id == botId);

            if (botInstance == null || string.IsNullOrWhiteSpace(message))
            {
                return;
            }
            var id = new SteamID();

            id.SetFromSteam3String(steamId);
            botInstance.SteamFriends.SendChatMessage(id, EChatEntryType.ChatMsg, message);
            if (logMessage)
            {
                var friendName = botInstance.SteamFriends.GetFriendPersonaName(id);
                _logger.Info($"#{botInstance.SequenceNumber} [Chat TX] To {friendName} ({steamId}): {message}");
            }
        }
Beispiel #13
0
        private static bool TrySetSteamID(string input, out SteamID steamID)
        {
            steamID = new SteamID();

            if (steamID.SetFromString(input, EUniverse.Public) ||
                steamID.SetFromSteam3String(input))
            {
                return(true);
            }

            if (ulong.TryParse(input, out var numericInput))
            {
                steamID.SetFromUInt64(numericInput);

                return(true);
            }

            return(false);
        }
        private async void Button_Click(object sender, RoutedEventArgs e)
        {
            var    steam = new SteamID();
            string url;

            if (!SteamID.CommunityURLRegex.IsMatch(TxtSteam.Text))
            {
                if (!steam.SetFromString(TxtSteam.Text) && !steam.SetFromSteam3String(TxtSteam.Text))
                {
                    ToastManager.ShowToastAsync("Could not add cheater",
                                                $"Steam ID '{TxtSteam.Text}' is not valid.",
                                                Notifications.Wpf.Core.NotificationType.Warning);
                    return;
                }
                url = $"https://steamcommunity.com/profiles/{steam.ConvertToUInt64()}/?xml=1";
            }
            else
            {
                url = $"{SteamID.CommunityURLRegex.Match(TxtSteam.Text).Value}/?xml=1";
            }

            var data = await Steam.GetProfileDataAsync(url);

            int.TryParse(TxtThreat.Text, out int threat);
            Cheater cheater = new()
            {
                AccountID     = Convert.ToUInt64(data.steamID64),
                CheatList     = TxtCheats.Text,
                LastKnownName = data.steamID,
                Submitter     = TxtSubmitter.Text,
                ThreatLevel   = threat,
                Notes         = TxtNotes.Text
            };

            Cheaters.Add(cheater);
            ToastManager.ShowToastAsync("Successfully added cheater",
                                        $"Hacker '{data.steamID}' has been added to the list.",
                                        Notifications.Wpf.Core.NotificationType.Success);
        }
Beispiel #15
0
        internal SteamID GetLastLoggedSteamID()
        {
            var ActiveUser = Microsoft.Win32.Registry.CurrentUser.OpenSubKey("SOFTWARE").OpenSubKey("Valve")?.OpenSubKey("Steam")?.OpenSubKey("ActiveProcess");

            var userID = ActiveUser?.GetValue("ActiveUser").ToString();
            int type   = 0;

            switch (ActiveUser?.GetValue("Universe").ToString())
            {
            case "Public":
                type = 1;
                break;

            case "Beta":
                type = 2;
                break;

            case "Internal":
                type = 3;
                break;

            case "Dev":
                type = 4;
                break;

            case "RC":
                type = 5;
                break;

            default:
                type = 0;
                break;
            }
            userID = "[U:" + type + ":" + userID + "]";
            var steamid = new SteamID();

            steamid.SetFromSteam3String(userID);
            return(steamid);
        }
Beispiel #16
0
        public void SetFromSteam3StringHandlesInvalid()
        {
            SteamID sid = new SteamID();

            bool setFromNullString = sid.SetFromSteam3String(null);

            Assert.False(setFromNullString);

            bool setFromEmptyString = sid.SetFromSteam3String("");

            Assert.False(setFromEmptyString);

            bool setFromInvalidString = sid.SetFromSteam3String("NOT A STEAMID!");

            Assert.False(setFromInvalidString);

            bool setFromInvalidAccountId = sid.SetFromSteam3String("STEAM_0:1:999999999999999999999999999999");

            Assert.False(setFromInvalidAccountId);

            bool setFromSteam2String = sid.SetFromSteam3String("STEAM_0:1:4491990");

            Assert.False(setFromSteam2String);

            bool mixingBracketsAndColons1 = sid.SetFromSteam3String("[A:1:2:345)]");

            Assert.False(mixingBracketsAndColons1);

            bool mixingBracketsAndColons2 = sid.SetFromSteam3String("[A:1:2(345]");

            Assert.False(mixingBracketsAndColons2);

            bool universeOutOfRange = sid.SetFromSteam3String("[U:5:123]");

            Assert.False(universeOutOfRange);
        }
Beispiel #17
0
        public void SetFromStringHandlesInvalid()
        {
            SteamID sid = new SteamID();

            bool setFromNullString = sid.SetFromString(null, EUniverse.Public);

            Assert.False(setFromNullString);

            bool setFromEmptyString = sid.SetFromString("", EUniverse.Public);

            Assert.False(setFromEmptyString);

            bool setFromInvalidString = sid.SetFromString("NOT A STEAMID!", EUniverse.Public);

            Assert.False(setFromInvalidString);

            bool setFromInvalidAccountId = sid.SetFromString("STEAM_0:1:999999999999999999999999999999", EUniverse.Public);

            Assert.False(setFromInvalidAccountId);

            bool universeOutOfRange = sid.SetFromSteam3String("STEAM_5:0:123");

            Assert.False(universeOutOfRange);
        }
Beispiel #18
0
        private void OnDelayedActionReceived(object sender, BasicDeliverEventArgs basicDeliverEventArgs)
        {
            try
            {
                using (var streamReader = new StreamReader(new MemoryStream(basicDeliverEventArgs.Body)))
                {
                    var serializer = new JsonSerializer();
                    var actionDto  =
                        serializer.Deserialize <SteamBotDelayedActionDto>(new JsonTextReader(streamReader));
                    switch (actionDto.Type)
                    {
                    case SteamBotDelayedActionType.SendChatMessage:
                    {
                        var steamId = new SteamID();
                        steamId.SetFromSteam3String((string)actionDto.Properties.SteamId);
                        var message = (string)actionDto.Properties.Message;
                        SteamFriends.SendChatMessage(steamId, EChatEntryType.ChatMsg, message);
                        try
                        {
                            if ((bool)actionDto.Properties.LogMessage)
                            {
                                var friendName = SteamFriends.GetFriendPersonaName(steamId);
                                _logger.Info($"#{SequenceNumber} [Chat TX] To {friendName} ({steamId}): {message}");
                            }
                        }
                        catch (RuntimeBinderException)
                        {
                        }
                        break;
                    }

                    case SteamBotDelayedActionType.RemoveFriend:
                    {
                        var steamId = new SteamID();
                        steamId.SetFromSteam3String((string)actionDto.Properties.SteamId);
                        var skip = SteamFriends.GetFriendRelationship(steamId) != EFriendRelationship.Friend;
                        try
                        {
                            if (!skip && (bool)actionDto.Properties.OnlyIfNotKeylolUser)
                            {
                                var result = _coordinator.Consume(
                                    coordinator => coordinator.IsKeylolUser(steamId.Render(true), Id));
                                if (!result.HasNoException || result.Value)
                                {
                                    skip = true;
                                }
                            }
                        }
                        catch (RuntimeBinderException)
                        {
                        }
                        if (!skip)
                        {
                            try
                            {
                                SteamFriends.SendChatMessage(steamId, EChatEntryType.ChatMsg,
                                                             (string)actionDto.Properties.Message);
                            }
                            catch (RuntimeBinderException)
                            {
                            }
                            SteamFriends.RemoveFriend(steamId);
                        }
                        break;
                    }

                    default:
                        _logger.Fatal($"#{SequenceNumber} Invalid delay action type: {actionDto.Type}.");
                        break;
                    }
                    _mqChannel.BasicAck(basicDeliverEventArgs.DeliveryTag, false);
                }
            }
            catch (Exception e)
            {
                _mqChannel.BasicNack(basicDeliverEventArgs.DeliveryTag, false, false);
                _logger.Fatal($"#{SequenceNumber} Unhandled MQ consumer exception.", e);
            }
        }
Beispiel #19
0
        public async Task <IHttpActionResult> UpdateListByCurrentUser(bool manual = false)
        {
            var userId = User.Identity.GetUserId();
            var user   = await _userManager.FindByIdAsync(userId);

            if (manual)
            {
                if (user.LastGameUpdateSucceed && DateTime.Now - user.LastGameUpdateTime < TimeSpan.FromMinutes(1))
                {
                    return(NotFound());
                }
            }
            else
            {
                if (!user.AutoSubscribeEnabled ||
                    (DateTime.Now - user.LastGameUpdateTime < TimeSpan.FromDays(user.AutoSubscribeDaySpan) &&
                     user.LastGameUpdateSucceed))
                {
                    return(NotFound());
                }
            }

            user.LastGameUpdateTime    = DateTime.Now;
            user.LastGameUpdateSucceed = true;
            await _dbContext.SaveChangesAsync(KeylolDbContext.ConcurrencyStrategy.ClientWin);

            try
            {
                var steamId = new SteamID();
                steamId.SetFromSteam3String(await _userManager.GetSteamIdAsync(user.Id));
                string allGamesHtml;
                if (user.SteamBotId != null && user.SteamBot.IsOnline())
                {
                    var botCoordinator = SteamBotCoordinator.Sessions[user.SteamBot.SessionId];
                    allGamesHtml = await botCoordinator.Client.Curl(user.SteamBotId,
                                                                    $"http://steamcommunity.com/profiles/{steamId.ConvertToUInt64()}/games/?tab=all&l=english");
                }
                else
                {
                    var httpClient = new HttpClient {
                        Timeout = TimeSpan.FromSeconds(10)
                    };
                    allGamesHtml = await httpClient.GetStringAsync(
                        $"http://steamcommunity.com/profiles/{steamId.ConvertToUInt64()}/games/?tab=all&l=english");
                }
                if (!string.IsNullOrWhiteSpace(allGamesHtml))
                {
                    var match = Regex.Match(allGamesHtml, @"<script language=""javascript"">\s*var rgGames = (.*)");
                    if (match.Success)
                    {
                        var trimed = match.Groups[1].Value.Trim();
                        var games  = JArray.Parse(trimed.Substring(0, trimed.Length - 1));
                        foreach (var game in games)
                        {
                            // game["hours"] 两周游戏时间
                            var appId = (int)game["appid"];

                            double totalPlayedTime = 0;
                            if (game["hours_forever"] != null)
                            {
                                totalPlayedTime = (double)game["hours_forever"];
                            }
                            if (Math.Abs(totalPlayedTime) <= 0.5)
                            {
                                continue;
                            }

                            var record = await _dbContext.UserGameRecords
                                         .Where(r => r.UserId == userId && r.SteamAppId == appId)
                                         .SingleOrDefaultAsync();

                            if (record == null)
                            {
                                record            = _dbContext.UserGameRecords.Create();
                                record.UserId     = userId;
                                record.SteamAppId = appId;
                                _dbContext.UserGameRecords.Add(record);
                            }
                            record.TotalPlayedTime = totalPlayedTime;

                            if (game["last_played"] != null)
                            {
                                record.LastPlayTime =
                                    Helpers.DateTimeFromTimeStamp((int)game["last_played"]);
                            }
                        }
                        await _dbContext.SaveChangesAsync();

                        var gameEntries = await _dbContext.UserGameRecords
                                          .Where(r => r.UserId == userId)
                                          .OrderByDescending(r => r.TotalPlayedTime)
                                          .Select(r =>
                                                  new
                        {
                            record = r,
                            point  = _dbContext.NormalPoints.FirstOrDefault(
                                p => p.Type == NormalPointType.Game && p.SteamAppId == r.SteamAppId)
                        })
                                          .Where(e => e.point != null)
                                          .ToListAsync();

                        var mostPlayed = gameEntries.Where(g =>
                                                           !user.SubscribedPoints.OfType <Models.NormalPoint>().Contains(g.point))
                                         .Select(g => g.point).Take(6).ToList();
                        var recentPlayed = gameEntries.Where(g =>
                                                             !user.SubscribedPoints.OfType <Models.NormalPoint>().Contains(g.point) &&
                                                             !mostPlayed.Contains(g.point))
                                           .OrderByDescending(g => g.record.LastPlayTime)
                                           .Select(g => g.point).Take(3).ToList();
                        var genreStats        = new Dictionary <Models.NormalPoint, int>();
                        var manufacturerStats = new Dictionary <Models.NormalPoint, int>();
                        foreach (var game in gameEntries)
                        {
                            foreach (var point in game.point.TagPoints
                                     .Concat(game.point.SeriesPoints)
                                     .Concat(game.point.GenrePoints)
                                     .Where(p => !user.SubscribedPoints.OfType <Models.NormalPoint>().Contains(p)))
                            {
                                if (genreStats.ContainsKey(point))
                                {
                                    genreStats[point]++;
                                }
                                else
                                {
                                    genreStats[point] = 1;
                                }
                            }
                            foreach (var point in game.point.DeveloperPoints
                                     .Concat(game.point.PublisherPoints)
                                     .Where(p => !user.SubscribedPoints.OfType <Models.NormalPoint>().Contains(p)))
                            {
                                if (manufacturerStats.ContainsKey(point))
                                {
                                    manufacturerStats[point]++;
                                }
                                else
                                {
                                    manufacturerStats[point] = 1;
                                }
                            }
                        }
                        var genres = genreStats.OrderByDescending(pair => pair.Value)
                                     .Select(pair => pair.Key).Take(3).ToList();
                        var manufactures = manufacturerStats.OrderByDescending(pair => pair.Value)
                                           .Select(pair => pair.Key).Take(3).ToList();
                        _dbContext.AutoSubscriptions.RemoveRange(
                            await _dbContext.AutoSubscriptions.Where(s => s.UserId == userId).ToListAsync());
                        var i = 0;
                        _dbContext.AutoSubscriptions.AddRange(
                            mostPlayed.Select(p => new { p, t = AutoSubscriptionType.MostPlayed })
                            .Concat(recentPlayed.Select(p => new { p, t = AutoSubscriptionType.RecentPlayed }))
                            .Concat(genres.Select(p => new { p, t = AutoSubscriptionType.Genre }))
                            .Concat(manufactures.Select(p => new { p, t = AutoSubscriptionType.Manufacture }))
                            .Select(e =>
                        {
                            var subscription           = _dbContext.AutoSubscriptions.Create();
                            subscription.UserId        = userId;
                            subscription.NormalPointId = e.p.Id;
                            subscription.Type          = e.t;
                            subscription.DisplayOrder  = i;
                            i++;
                            return(subscription);
                        }));
                        await _dbContext.SaveChangesAsync();

                        return(Ok(new
                        {
                            MostPlayed = mostPlayed.Select(p => new NormalPointDto(p)),
                            RecentPlayed = recentPlayed.Select(p => new NormalPointDto(p)),
                            Genres = genres.Select(p => new NormalPointDto(p)),
                            Manufacturers = manufactures.Select(p => new NormalPointDto(p))
                        }));
                    }
                    if (Regex.IsMatch(allGamesHtml, @"This profile is private\."))
                    {
                        return(Unauthorized());
                    }
                    throw new Exception();
                }
            }
            catch (Exception)
            {
                user.LastGameUpdateSucceed = false;
                await _dbContext.SaveChangesAsync(KeylolDbContext.ConcurrencyStrategy.ClientWin);
            }
            return(NotFound());
        }
        /// <summary>
        /// 抓取指定用户的 Steam App 库
        /// </summary>
        /// <param name="userId">用户 ID</param>
        /// <param name="dbContext"><see cref="KeylolDbContext"/></param>
        /// <param name="userManager"><see cref="KeylolUserManager"/></param>
        /// <param name="redis"><see cref="RedisProvider"/></param>
        /// <param name="cachedData"><see cref="CachedDataProvider.CachedDataProvider"/></param>
        /// <returns>如果抓取成功,返回 <c>true</c></returns>
        public static async Task <bool> UpdateUserSteamGameRecordsAsync([NotNull] string userId,
                                                                        KeylolDbContext dbContext, KeylolUserManager userManager, RedisProvider redis,
                                                                        CachedDataProvider.CachedDataProvider cachedData)
        {
            var cacheKey    = UserSteamGameRecordsCrawlerStampCacheKey(userId);
            var redisDb     = redis.GetDatabase();
            var cacheResult = await redisDb.StringGetAsync(cacheKey);

            if (cacheResult.HasValue)
            {
                return(false);
            }
            await redisDb.StringSetAsync(cacheKey, DateTime.Now.ToTimestamp(), UserSteamGameRecordsUpdatePeriod);

            try
            {
                var user = await userManager.FindByIdAsync(userId);

                var steamId = new SteamID();
                steamId.SetFromSteam3String(await userManager.GetSteamIdAsync(user.Id));
                string allGamesHtml;
                if (user.SteamBotId != null && user.SteamBot.IsOnline())
                {
                    var botCoordinator = SteamBotCoordinator.Sessions[user.SteamBot.SessionId];
                    allGamesHtml = await botCoordinator.Client.Curl(user.SteamBotId,
                                                                    $"http://steamcommunity.com/profiles/{steamId.ConvertToUInt64()}/games/?tab=all&l=english");
                }
                else
                {
                    allGamesHtml = await HttpClient.GetStringAsync(
                        $"http://steamcommunity.com/profiles/{steamId.ConvertToUInt64()}/games/?tab=all&l=english");
                }
                if (string.IsNullOrWhiteSpace(allGamesHtml))
                {
                    throw new Exception();
                }
                var match = Regex.Match(allGamesHtml, @"<script language=""javascript"">\s*var rgGames = (.*)");
                if (!match.Success)
                {
                    throw new Exception();
                }
                var trimed     = match.Groups[1].Value.Trim();
                var games      = JArray.Parse(trimed.Substring(0, trimed.Length - 1));
                var oldRecords = (await dbContext.UserSteamGameRecords.Where(r => r.UserId == user.Id).ToListAsync())
                                 .ToDictionary(r => r.SteamAppId, r => r);
                foreach (var game in games)
                {
                    var appId = (int)game["appid"];
                    UserSteamGameRecord record;
                    if (oldRecords.TryGetValue(appId, out record))
                    {
                        oldRecords.Remove(appId);
                    }
                    else
                    {
                        record = new UserSteamGameRecord
                        {
                            UserId     = user.Id,
                            SteamAppId = appId
                        };
                        dbContext.UserSteamGameRecords.Add(record);
                    }
                    record.TwoWeekPlayedTime = game["hours"] != null ? (double)game["hours"] : 0;
                    record.TotalPlayedTime   = game["hours_forever"] != null ? (double)game["hours_forever"] : 0;
                    if (game["last_played"] != null)
                    {
                        record.LastPlayTime = Helpers.DateTimeFromTimeStamp((ulong)game["last_played"]);
                    }
                }
                dbContext.UserSteamGameRecords.RemoveRange(oldRecords.Values);
                await dbContext.SaveChangesAsync();

                await cachedData.Users.PurgeSteamAppLibraryCacheAsync(userId);

                return(true);
            }
            catch (Exception)
            {
                await redisDb.KeyExpireAsync(cacheKey, SilenceTime);

                return(false);
            }
        }