예제 #1
0
        private void OnTimer(object state)
        {
            if (Settings.Current.IRC.Enabled && !IRC.Instance.IsConnected)
            {
                Log.WriteWarn("Watchdog", "Forcing IRC reconnect.");

                IRC.Instance.Connect();
            }

            if (Steam.Instance.Client.IsConnected && Application.ChangelistTimer.Enabled)
            {
                AccountInfo.Sync();

                if (WebAuth.IsAuthorized)
                {
                    TaskManager.RunAsync(async() => await AccountInfo.RefreshAppsToIdle());
                }
                else
                {
                    WebAuth.AuthenticateUser();
                }
            }
            else if (DateTime.Now.Subtract(Connection.LastSuccessfulLogin).TotalMinutes >= 5.0)
            {
                Log.WriteWarn("Watchdog", "Forcing a Steam reconnect.");

                Connection.Reconnect(null, null);
            }
        }
예제 #2
0
        public async static Task RefreshAppsToIdle()
        {
            if (!Settings.Current.CanQueryStore)
            {
                return;
            }

            List <uint> newAppsToIdle;

            using (var handler = new HttpClientHandler())
                using (var client = new HttpClient(handler))
                {
                    handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
                    client.DefaultRequestHeaders.Add("Host", "steamdb.info");

                    var response = await client.GetAsync("https://localhost/api/GetNextAppIdToIdle/");

                    if (!response.IsSuccessStatusCode)
                    {
                        Log.WriteWarn("AccountInfo", $"GetNextAppIdToIdle returned {response.StatusCode}");

                        if (response.StatusCode == HttpStatusCode.Unauthorized)
                        {
                            await WebAuth.AuthenticateUser();
                        }

                        return;
                    }

                    var data = await response.Content.ReadAsStringAsync();

                    newAppsToIdle = JsonConvert.DeserializeObject <List <uint> >(data);
                }

            Log.WriteInfo("AccountInfo", $"{newAppsToIdle.Count} apps to idle: {string.Join(", ", newAppsToIdle)}");

            if (!AppsToIdle.SequenceEqual(newAppsToIdle))
            {
                AppsToIdle = newAppsToIdle;
                Sync();
            }
        }
예제 #3
0
        private async Task RequestBetas()
        {
            var removed = false;

            foreach (var appId in BetasToRequest)
            {
                Log.WriteDebug(nameof(FreeLicense), $"Requesting beta {appId}");

                try
                {
                    var response = await WebAuth.PerformRequest(
                        HttpMethod.Post,
                        new Uri($"https://store.steampowered.com/ajaxrequestplaytestaccess/{appId}"),
                        new List <KeyValuePair <string, string> >
                    {
                        new KeyValuePair <string, string>("sessionid", nameof(SteamDatabaseBackend))
                    }
                        );

                    var data = await response.Content.ReadAsStringAsync();

                    BetasToRequest.Remove(appId);
                    removed = true;

                    Log.WriteDebug(nameof(FreeLicense), $"Beta {appId}: {data}");
                }
                catch (Exception e)
                {
                    Log.WriteWarn(nameof(FreeLicense), $"Failed to request beta {appId}: {e.Message}");
                }
            }

            if (removed)
            {
                await SaveBetas();
            }
        }
예제 #4
0
        private static void OnFreeLicenseCallback(SteamApps.FreeLicenseCallback callback)
        {
            JobManager.TryRemoveJob(callback.JobID);

            var packageIDs = callback.GrantedPackages;
            var appIDs     = callback.GrantedApps;

            Log.WriteDebug("FreeLicense", "Received free license: {0} ({1} apps: {2}, {3} packages: {4})",
                           callback.Result, appIDs.Count, string.Join(", ", appIDs), packageIDs.Count, string.Join(", ", packageIDs));

            if (appIDs.Count > 0)
            {
                JobManager.AddJob(() => Steam.Instance.Apps.PICSGetAccessTokens(appIDs, Enumerable.Empty <uint>()));
            }

            if (packageIDs.Count > 0)
            {
                JobManager.AddJob(() => Steam.Instance.Apps.PICSGetProductInfo(Enumerable.Empty <uint>(), packageIDs));

                // We don't want to block our main thread with web requests
                TaskManager.Run(() =>
                {
                    string data = null;

                    try
                    {
                        var response = WebAuth.PerformRequest("GET", "https://store.steampowered.com/account/licenses/");

                        using (var responseStream = response.GetResponseStream())
                        {
                            using (var reader = new StreamReader(responseStream))
                            {
                                data = reader.ReadToEnd();
                            }
                        }
                    }
                    catch (WebException e)
                    {
                        Log.WriteError("FreeLicense", "Failed to fetch account details page: {0}", e.Message);
                    }

                    using (var db = Database.GetConnection())
                    {
                        foreach (var package in packageIDs)
                        {
                            var packageData = db.Query <Package>("SELECT `SubID`, `Name`, `LastKnownName` FROM `Subs` WHERE `SubID` = @SubID", new { SubID = package }).FirstOrDefault();

                            if (!string.IsNullOrEmpty(data))
                            {
                                // Tell me all about using regex
                                var match = Regex.Match(data, string.Format("RemoveFreeLicense\\( ?{0}, ?'(.+)' ?\\)", package));

                                if (match.Success)
                                {
                                    var grantedName = Encoding.UTF8.GetString(Convert.FromBase64String(match.Groups[1].Value));

                                    // Update last known name if we can
                                    if (packageData.SubID > 0 && (string.IsNullOrEmpty(packageData.LastKnownName) || packageData.LastKnownName.StartsWith("Steam Sub ", StringComparison.Ordinal)))
                                    {
                                        db.Execute("UPDATE `Subs` SET `LastKnownName` = @Name WHERE `SubID` = @SubID", new { SubID = package, Name = grantedName });

                                        db.Execute(SubProcessor.GetHistoryQuery(),
                                                   new PICSHistory
                                        {
                                            ID       = package,
                                            Key      = SteamDB.DATABASE_NAME_TYPE,
                                            OldValue = "free on demand; account page",
                                            NewValue = grantedName,
                                            Action   = "created_info"
                                        }
                                                   );

                                        // Add a app comment on each app in this package
                                        var comment = string.Format("This app is in a free on demand package called <b>{0}</b>", SecurityElement.Escape(grantedName));
                                        var apps    = db.Query <PackageApp>("SELECT `AppID` FROM `SubsApps` WHERE `SubID` = @SubID", new { SubID = package }).ToList();
                                        var types   = db.Query <App>("SELECT `AppID` FROM `Apps` WHERE `AppType` > 0 AND `AppID` IN @Ids", new { Ids = apps.Select(x => x.AppID) }).ToDictionary(x => x.AppID, x => true);
                                        var key     = db.ExecuteScalar <uint>("SELECT `ID` FROM `KeyNames` WHERE `Name` = 'website_comment'");

                                        foreach (var app in apps)
                                        {
                                            if (types.ContainsKey(app.AppID))
                                            {
                                                continue;
                                            }

                                            db.Execute("INSERT INTO `AppsInfo` VALUES (@AppID, @Key, @Value) ON DUPLICATE KEY UPDATE `Key` = `Key`", new { app.AppID, Key = key, value = comment });
                                        }
                                    }

                                    packageData.LastKnownName = grantedName;
                                }
                            }

                            IRC.Instance.SendMain("New free license granted: {0}{1}{2} -{3} {4}",
                                                  Colors.BLUE, Steam.FormatPackageName(package, packageData), Colors.NORMAL,
                                                  Colors.DARKBLUE, SteamDB.GetPackageURL(package)
                                                  );
                        }
                    }
                });
            }
        }
예제 #5
0
        private async Task RefreshPackageNames()
        {
            if (CurrentlyUpdatingNames)
            {
                return;
            }

            string data;

            try
            {
                CurrentlyUpdatingNames = true;

                var response = await WebAuth.PerformRequest(HttpMethod.Get, "https://store.steampowered.com/account/licenses/");

                data = await response.Content.ReadAsStringAsync();
            }
            catch (Exception e)
            {
                Log.WriteError(nameof(FreeLicense), $"Failed to fetch account details page: {e.Message}");

                return;
            }
            finally
            {
                CurrentlyUpdatingNames = false;
            }

            var matches = PackageRegex.Matches(data);
            var names   = new Dictionary <uint, string>();

            foreach (Match match in matches)
            {
                var subID = uint.Parse(match.Groups["subid"].Value);
                var name  = Encoding.UTF8.GetString(Convert.FromBase64String(match.Groups["name"].Value));

                names[subID] = name;
            }

            if (names.Count == 0)
            {
                Log.WriteError(nameof(FreeLicense), "Failed to find any package names on licenses page");

                return;
            }

            // Skip packages that have a store name to avoid messing up history
            await using var db = await Database.GetConnectionAsync();

            var packageData = await db.QueryAsync <Package>("SELECT `SubID`, `LastKnownName` FROM `Subs` WHERE `SubID` IN @Ids AND `StoreName` = ''", new { Ids = names.Keys });

            foreach (var package in packageData)
            {
                var newName = names[package.SubID];

                if (package.LastKnownName != newName)
                {
                    Log.WriteInfo(nameof(FreeLicense), $"Changed package name for {package.SubID} from \"{package.LastKnownName}\" to \"{newName}\"");

                    await db.ExecuteAsync("UPDATE `Subs` SET `LastKnownName` = @Name WHERE `SubID` = @SubID", new { package.SubID, Name = newName });

                    await db.ExecuteAsync(
                        SubProcessor.HistoryQuery,
                        new PICSHistory
                    {
                        ID       = package.SubID,
                        Key      = SteamDB.DatabaseNameType,
                        OldValue = "free on demand; account page",
                        NewValue = newName,
                        Action   = "created_info"
                    }
                        );
                }
            }
        }
예제 #6
0
        private void RefreshPackageNames()
        {
            if (CurrentlyUpdatingNames)
            {
                return;
            }

            string data;

            try
            {
                CurrentlyUpdatingNames = true;

                var response = WebAuth.PerformRequest("GET", "https://store.steampowered.com/account/licenses/");

                using (var responseStream = response.GetResponseStream())
                {
                    using (var reader = new StreamReader(responseStream))
                    {
                        data = reader.ReadToEnd();
                    }
                }
            }
            catch (WebException e)
            {
                Log.WriteError("FreeLicense", "Failed to fetch account details page: {0}", e.Message);

                return;
            }
            finally
            {
                CurrentlyUpdatingNames = false;
            }

            var matches = PackageRegex.Matches(data);
            var names   = new Dictionary <uint, string>();

            foreach (Match match in matches)
            {
                var subID = uint.Parse(match.Groups["subid"].Value);
                var name  = Encoding.UTF8.GetString(Convert.FromBase64String(match.Groups["name"].Value));

                names[subID] = name;
            }

            using (var db = Database.Get())
            {
                // Skip packages that have a store name to avoid messing up history
                var packageData = db.Query <Package>("SELECT `SubID`, `LastKnownName` FROM `Subs` WHERE `SubID` IN @Ids AND `StoreName` = ''", new { Ids = names.Keys });

                foreach (var package in packageData)
                {
                    var newName = names[package.SubID];

                    if (package.LastKnownName != newName)
                    {
                        Log.WriteInfo("FreeLicense", "Changed package name for {0} from \"{1}\" to \"{2}\"", package.SubID, package.LastKnownName, newName);

                        db.Execute("UPDATE `Subs` SET `LastKnownName` = @Name WHERE `SubID` = @SubID", new { package.SubID, Name = newName });

                        db.Execute(
                            SubProcessor.HistoryQuery,
                            new PICSHistory
                        {
                            ID       = package.SubID,
                            Key      = SteamDB.DATABASE_NAME_TYPE,
                            OldValue = "free on demand; account page",
                            NewValue = newName,
                            Action   = "created_info"
                        }
                            );
                    }
                }
            }
        }
        protected override async Task ProcessData()
        {
            ChangeNumber = ProductInfo.ChangeNumber;

            if (Settings.IsFullRun)
            {
                await DbConnection.ExecuteAsync("INSERT INTO `Changelists` (`ChangeID`) VALUES (@ChangeNumber) ON DUPLICATE KEY UPDATE `Date` = `Date`", new { ProductInfo.ChangeNumber });

                await DbConnection.ExecuteAsync("INSERT INTO `ChangelistsApps` (`ChangeID`, `AppID`) VALUES (@ChangeNumber, @AppID) ON DUPLICATE KEY UPDATE `AppID` = `AppID`", new { AppID, ProductInfo.ChangeNumber });
            }

            await ProcessKey("root_changenumber", "changenumber", ChangeNumber.ToString());

            var app = (await DbConnection.QueryAsync <App>("SELECT `Name`, `AppType` FROM `Apps` WHERE `AppID` = @AppID LIMIT 1", new { AppID })).SingleOrDefault();

            var newAppName = ProductInfo.KeyValues["common"]["name"].AsString();
            var newAppType = -1;

            if (newAppName != null)
            {
                var currentType = ProductInfo.KeyValues["common"]["type"].AsString().ToLowerInvariant();

                newAppType = await DbConnection.ExecuteScalarAsync <int?>("SELECT `AppType` FROM `AppsTypes` WHERE `Name` = @Type LIMIT 1", new { Type = currentType }) ?? -1;

                var modifiedNameOrType = false;

                if (newAppType == -1)
                {
                    await DbConnection.ExecuteAsync("INSERT INTO `AppsTypes` (`Name`, `DisplayName`) VALUES(@Name, @DisplayName)",
                                                    new { Name = currentType, DisplayName = ProductInfo.KeyValues["common"]["type"].AsString() }); // We don't need to lower display name

                    Log.WriteInfo(nameof(AppProcessor), $"Creating new apptype \"{currentType}\" (AppID {AppID})");

                    IRC.Instance.SendOps($"New app type: {Colors.BLUE}{currentType}{Colors.NORMAL} - {SteamDB.GetAppUrl(AppID, "history")}");

                    newAppType = await DbConnection.ExecuteScalarAsync <int>("SELECT `AppType` FROM `AppsTypes` WHERE `Name` = @Type LIMIT 1", new { Type = currentType });
                }

                if (string.IsNullOrEmpty(app.Name) || app.Name.StartsWith(SteamDB.UnknownAppName, StringComparison.Ordinal))
                {
                    await DbConnection.ExecuteAsync("INSERT INTO `Apps` (`AppID`, `AppType`, `Name`, `LastKnownName`) VALUES (@AppID, @Type, @AppName, @AppName) ON DUPLICATE KEY UPDATE `Name` = VALUES(`Name`), `LastKnownName` = VALUES(`LastKnownName`), `AppType` = VALUES(`AppType`)",
                                                    new { AppID, Type = newAppType, AppName = newAppName }
                                                    );

                    await MakeHistory("created_app");
                    await MakeHistory("created_info", SteamDB.DatabaseNameType, string.Empty, newAppName);

                    modifiedNameOrType = true;
                }
                else if (app.Name != newAppName)
                {
                    await DbConnection.ExecuteAsync("UPDATE `Apps` SET `Name` = @AppName, `LastKnownName` = @AppName WHERE `AppID` = @AppID", new { AppID, AppName = newAppName });
                    await MakeHistory("modified_info", SteamDB.DatabaseNameType, app.Name, newAppName);

                    modifiedNameOrType = true;
                }

                if (app.AppType == 0 || app.AppType != newAppType)
                {
                    await DbConnection.ExecuteAsync("UPDATE `Apps` SET `AppType` = @Type WHERE `AppID` = @AppID", new { AppID, Type = newAppType });

                    if (app.AppType == 0)
                    {
                        await MakeHistory("created_info", SteamDB.DatabaseAppType, string.Empty, newAppType.ToString());
                    }
                    else
                    {
                        await MakeHistory("modified_info", SteamDB.DatabaseAppType, app.AppType.ToString(), newAppType.ToString());
                    }

                    modifiedNameOrType = true;
                }

                if (modifiedNameOrType)
                {
                    if ((newAppType > 9 && newAppType != 13 && newAppType != 15 && newAppType != 17 && newAppType != 18) || Triggers.Any(newAppName.Contains))
                    {
                        IRC.Instance.SendOps($"New {currentType}: {Colors.BLUE}{Utils.LimitStringLength(newAppName)}{Colors.NORMAL} -{Colors.DARKBLUE} {SteamDB.GetAppUrl(AppID, "history")}");
                    }
                }
            }

            foreach (var section in ProductInfo.KeyValues.Children)
            {
                var sectionName = section.Name.ToLowerInvariant();

                if (sectionName == "appid" || sectionName == "change_number")
                {
                    continue;
                }

                if (sectionName == "common" || sectionName == "extended")
                {
                    foreach (var keyvalue in section.Children)
                    {
                        var keyName = $"{sectionName}_{keyvalue.Name}";

                        if (keyName == "common_type" || keyName == "common_gameid" || keyName == "common_name" || keyName == "extended_order")
                        {
                            // Ignore common keys that are either duplicated or serve no real purpose
                            continue;
                        }

                        if (keyvalue.Children.Count > 0)
                        {
                            await ProcessKey(keyName, keyvalue.Name, Utils.JsonifyKeyValue(keyvalue), keyvalue);
                        }
                        else if (!string.IsNullOrEmpty(keyvalue.Value))
                        {
                            await ProcessKey(keyName, keyvalue.Name, keyvalue.Value);
                        }
                    }
                }
                else if (sectionName == "public_only")
                {
                    await ProcessKey($"root_{sectionName}", section.Name, section.Value);
                }
                else
                {
                    sectionName = $"root_{sectionName}";

                    if (await ProcessKey(sectionName, sectionName, Utils.JsonifyKeyValue(section), section) && sectionName == "root_depots")
                    {
                        await DbConnection.ExecuteAsync("UPDATE `Apps` SET `LastDepotUpdate` = CURRENT_TIMESTAMP() WHERE `AppID` = @AppID", new { AppID });
                    }
                }
            }

            // If app gets hidden but we already have data, do not delete the already existing app info
            if (newAppName != null)
            {
                foreach (var data in CurrentData.Values.Where(data => !data.Processed && !data.KeyName.StartsWith("website", StringComparison.Ordinal)))
                {
                    await DbConnection.ExecuteAsync("DELETE FROM `AppsInfo` WHERE `AppID` = @AppID AND `Key` = @Key", new { AppID, data.Key });
                    await MakeHistory("removed_key", data.Key, data.Value);
                }
            }
            else
            {
                if (string.IsNullOrEmpty(app.Name)) // We don't have the app in our database yet
                {
                    await DbConnection.ExecuteAsync("INSERT INTO `Apps` (`AppID`, `Name`) VALUES (@AppID, @AppName) ON DUPLICATE KEY UPDATE `AppType` = `AppType`", new {
                        AppID,
                        AppName = $"{SteamDB.UnknownAppName} {AppID}"
                    });
                }
                else if (!app.Name.StartsWith(SteamDB.UnknownAppName, StringComparison.Ordinal)) // We do have the app, replace it with default name
                {
                    await DbConnection.ExecuteAsync("UPDATE `Apps` SET `Name` = @AppName, `AppType` = 0 WHERE `AppID` = @AppID", new {
                        AppID,
                        AppName = $"{SteamDB.UnknownAppName} {AppID}"
                    });
                    await MakeHistory("deleted_app", 0, app.Name);
                }
            }

            // Close the connection as it's no longer needed going into depot processor
            DbConnection.Close();

            if (ProductInfo.KeyValues["depots"].Children.Any())
            {
                await Steam.Instance.DepotProcessor.Process(AppID, ChangeNumber, ProductInfo.KeyValues["depots"]);
            }

            if (ProductInfo.MissingToken && PICSTokens.HasAppToken(AppID))
            {
                Log.WriteError(nameof(PICSTokens), $"Overridden token for appid {AppID} is invalid?");
                IRC.Instance.SendOps($"[Tokens] Looks like the overridden token for appid {AppID} ({newAppName}) is invalid");
            }

            if (Settings.IsMillhaven && app.AppType == 0 && newAppType == 18)
            {
                var betaAppId = ProductInfo.KeyValues["extended"]["betaforappid"].AsUnsignedInteger();

                if (betaAppId == 0)
                {
                    betaAppId = ProductInfo.KeyValues["common"]["parent"].AsUnsignedInteger();
                }

                Log.WriteDebug(nameof(AppProcessor), $"Requesting beta access for {AppID} ({betaAppId})");

                var response = await WebAuth.PerformRequest(
                    HttpMethod.Post,
                    new Uri($"https://store.steampowered.com/ajaxrequestplaytestaccess/{betaAppId}"),
                    new List <KeyValuePair <string, string> >
                {
                    new KeyValuePair <string, string>("sessionid", nameof(SteamDatabaseBackend))
                }
                    );

                var data = await response.Content.ReadAsStringAsync();

                Log.WriteDebug(nameof(AppProcessor), $"Beta {AppID}: {data}");
            }
        }