Example #1
0
        public static async Task Init()
        {
            ImportantApps = new Dictionary <uint, List <string> >();
            ImportantSubs = new Dictionary <uint, byte>();

            await ReloadImportant();

            await KeyNameCache.Init();

            if (Settings.IsFullRun)
            {
                return;
            }

            var commandHandler = new CommandHandler();

            Steam.Instance.RegisterCommandHandlers(commandHandler);

            if (Settings.Current.IRC.Enabled)
            {
                RssReader = new RSS();

                IrcThread = new Thread(IRC.Instance.Connect)
                {
                    Name = "IRC"
                };
                IrcThread.Start();

                IRC.Instance.RegisterCommandHandlers(commandHandler);
            }
        }
        private void OnTimer(object sender, ElapsedEventArgs e)
        {
            if (!Steam.Instance.IsLoggedOn)
            {
                lock (FreeLicenseTimer)
                {
                    FreeLicenseTimer.Start();
                }

                return;
            }

            var list = FreeLicensesToRequest.Take(REQUEST_RATE_LIMIT).ToList();
            var now  = DateUtils.DateTimeToUnixTime(DateTime.UtcNow) - 60;
            Dictionary <uint, ulong> startTimes;

            using (var db = Database.Get())
            {
                startTimes = db.Query(
                    "SELECT `SubID`, `Value` FROM `SubsInfo` WHERE `Key` = @Key AND `SubID` IN @Ids",
                    new
                {
                    Key = KeyNameCache.GetSubKeyID("extended_starttime"),
                    Ids = list.Select(x => x.Key)
                }
                    ).ToDictionary(x => (uint)x.SubID, x => Convert.ToUInt64((string)x.Value));
            }

            foreach (var(subId, _) in list)
            {
                if (startTimes.TryGetValue(subId, out var startTime) && startTime > now)
                {
                    // If start time has not been reached yet, don't remove this app from the list and keep trying to activate it
                    continue;
                }

                FreeLicensesToRequest.TryRemove(subId, out _);
            }

            TaskManager.Run(Save);

            var appids = list.Select(x => x.Value).Distinct();

            AppsRequestedInHour = appids.Count();

            Log.WriteDebug(nameof(FreeLicense), $"Requesting {AppsRequestedInHour} free apps as the rate limit timer ran: {string.Join(", ", appids)}");

            JobManager.AddJob(() => Steam.Instance.Apps.RequestFreeLicense(appids));

            if (FreeLicensesToRequest.Count > 0)
            {
                lock (FreeLicenseTimer)
                {
                    FreeLicenseTimer.Start();
                }
            }
        }
        public static void Init()
        {
            ImportantApps = new Dictionary <uint, List <string> >();
            ImportantSubs = new Dictionary <uint, byte>();

            ReloadImportant();
            TaskManager.RunAsync(async() => await KeyNameCache.Init());

            ChangelistTimer          = new Timer();
            ChangelistTimer.Elapsed += Tick;
            ChangelistTimer.Interval = TimeSpan.FromSeconds(1).TotalMilliseconds;

            var thread = new Thread(Steam.Instance.Tick)
            {
                Name = "Steam"
            };

            thread.Start();

            var anonThread = new Thread(Steam.Anonymous.Tick)
            {
                Name = "SteamAnonymous"
            };

            anonThread.Start();

            Threads = new List <Thread>
            {
                thread,
                anonThread,
            };

            if (Settings.IsFullRun)
            {
                return;
            }

            var commandHandler = new CommandHandler();

            Steam.Instance.RegisterCommandHandlers(commandHandler);

            if (Settings.Current.IRC.Enabled)
            {
                RssReader = new RSS();

                thread = new Thread(IRC.Instance.Connect)
                {
                    Name = "IRC"
                };
                thread.Start();

                Threads.Add(thread);

                IRC.Instance.RegisterCommandHandlers(commandHandler);
            }
        }
Example #4
0
        public static void Init()
        {
            ImportantApps = new Dictionary <uint, List <string> >();
            ImportantSubs = new Dictionary <uint, byte>();

            ReloadImportant();
            TaskManager.RunAsync(async() => await KeyNameCache.Init());

            var thread = new Thread(Steam.Instance.Tick)
            {
                Name = "Steam"
            };

            thread.Start();

            Threads = new List <Thread>
            {
                thread,
            };

            if (Settings.Current.BuiltInHttpServerPort > 0)
            {
                HttpServer = new HttpServer(Settings.Current.BuiltInHttpServerPort);
            }

            if (Settings.IsFullRun)
            {
                return;
            }

            var commandHandler = new CommandHandler();

            Steam.Instance.RegisterCommandHandlers(commandHandler);

            if (Settings.Current.IRC.Enabled)
            {
                RssReader = new RSS();

                thread = new Thread(IRC.Instance.Connect)
                {
                    Name = "IRC"
                };
                thread.Start();

                Threads.Add(thread);

                IRC.Instance.RegisterCommandHandlers(commandHandler);
            }
        }
        public PICSChanges(CallbackManager manager)
        {
            if (Settings.IsFullRun)
            {
                var timer = new Timer
                {
                    Interval = TimeSpan.FromSeconds(10).TotalMilliseconds
                };
                timer.Elapsed += (sender, args) => FullUpdateProcessor.IsBusy();
                timer.Start();

                return;
            }

            manager.Subscribe <SteamApps.PICSChangesCallback>(OnPICSChanges);

            using (var db = Database.Get())
            {
                BillingTypeKey = KeyNameCache.GetSubKeyID("root_billingtype");

                if (LocalConfig.Current.ChangeNumber == 0)
                {
                    PreviousChangeNumber             = db.ExecuteScalar <uint>("SELECT `ChangeID` FROM `Changelists` ORDER BY `ChangeID` DESC LIMIT 1");
                    LocalConfig.Current.ChangeNumber = PreviousChangeNumber;
                }
                else
                {
                    PreviousChangeNumber = LocalConfig.Current.ChangeNumber;
                }

                Log.WriteInfo(nameof(PICSChanges), $"Previous changelist was {PreviousChangeNumber}");
            }

            if (PreviousChangeNumber == 0)
            {
                Log.WriteWarn(nameof(PICSChanges), "Looks like there are no changelists in the database.");
                Log.WriteWarn(nameof(PICSChanges), $"If you want to fill up your database first, restart with \"FullRun\" setting set to {(int)FullRunState.Enumerate}.");
            }
        }
Example #6
0
        public static async Task Init()
        {
            ImportantApps = new HashSet <uint>();
            ImportantSubs = new HashSet <uint>();

            await ReloadImportant();

            await PICSTokens.Reload();

            await KeyNameCache.Init();

            if (Settings.Current.BuiltInHttpServerPort > 0)
            {
                HttpServer = new HttpServer(Settings.Current.BuiltInHttpServerPort);
            }

            if (Settings.IsFullRun)
            {
                return;
            }

            var commandHandler = new CommandHandler();

            Steam.Instance.RegisterCommandHandlers(commandHandler);

            RssReader = new RSS();

            if (Settings.Current.IRC.Enabled)
            {
                IrcThread = new Thread(IRC.Instance.Connect)
                {
                    Name = nameof(IRC)
                };
                IrcThread.Start();

                IRC.Instance.RegisterCommandHandlers(commandHandler);
            }
        }
Example #7
0
        public static void RequestFromPackage(uint subId, KeyValue kv)
        {
            if ((EBillingType)kv["billingtype"].AsInteger() != EBillingType.FreeOnDemand)
            {
                return;
            }

            if (kv["appids"].Children.Count == 0)
            {
                Log.WriteDebug(nameof(FreeLicense), $"Package {subId} has no apps");
                return;
            }

            // TODO: Put LicenseList.OwnedApps.ContainsKey() in First() search
            var appId = kv["appids"].Children[0].AsUnsignedInteger();

            if (LicenseList.OwnedApps.ContainsKey(appId))
            {
                return;
            }

            if (kv["status"].AsInteger() != 0) // EPackageStatus.Available
            {
                Log.WriteDebug(nameof(FreeLicense), $"Package {subId} is not available");
                return;
            }

            if ((ELicenseType)kv["licensetype"].AsInteger() != ELicenseType.SinglePurchase)
            {
                Log.WriteDebug(nameof(FreeLicense), $"Package {subId} is not single purchase");
                return;
            }

            var dontGrantIfAppIdOwned = kv["extended"]["dontgrantifappidowned"].AsUnsignedInteger();

            if (dontGrantIfAppIdOwned > 0 && LicenseList.OwnedApps.ContainsKey(dontGrantIfAppIdOwned))
            {
                Log.WriteDebug(nameof(FreeLicense), $"Package {subId} already owns app {dontGrantIfAppIdOwned}");
                return;
            }

            if (kv["extended"]["curatorconnect"].AsInteger() == 1)
            {
                Log.WriteDebug(nameof(FreeLicense), $"Package {subId} is a curator package");
                return;
            }

            var allowPurchaseFromRestrictedCountries = kv["extended"]["allowpurchasefromrestrictedcountries"].AsBoolean();
            var purchaseRestrictedCountries          = kv["extended"]["purchaserestrictedcountries"].AsString();

            if (purchaseRestrictedCountries != null && purchaseRestrictedCountries.Contains(AccountInfo.Country) != allowPurchaseFromRestrictedCountries)
            {
                Log.WriteDebug(nameof(FreeLicense), $"Package {subId} is not available in {AccountInfo.Country}");
                return;
            }

            var startTime  = kv["extended"]["starttime"].AsUnsignedLong();
            var expiryTime = kv["extended"]["expirytime"].AsUnsignedLong();
            var now        = DateUtils.DateTimeToUnixTime(DateTime.UtcNow);

            if (expiryTime > 0 && expiryTime < now)
            {
                Log.WriteDebug(nameof(FreeLicense), $"Package {subId} has already expired");
                return;
            }

            if (startTime > 0)
            {
                if (startTime < now)
                {
                    QueueRequest(subId, appId);
                }
                else
                {
                    AddToQueue(subId, appId);
                }

                Log.WriteDebug(nameof(FreeLicense), $"Package {subId} has not reached starttime yet, added to queue");

                return;
            }

            uint parentAppId;
            bool available;

            using (var db = Database.Get())
            {
                available   = db.ExecuteScalar <bool>("SELECT IFNULL(`Value`, \"\") = \"released\" FROM `Apps` LEFT JOIN `AppsInfo` ON `Apps`.`AppID` = `AppsInfo`.`AppID` AND `Key` = @Key WHERE `Apps`.`AppID` = @AppID", new { Key = KeyNameCache.GetAppKeyID("common_releasestate"), AppID = appId });
                parentAppId = db.ExecuteScalar <uint>("SELECT `Value` FROM `Apps` JOIN `AppsInfo` ON `Apps`.`AppID` = `AppsInfo`.`AppID` WHERE `Key` = @Key AND `Apps`.`AppID` = @AppID AND `AppType` NOT IN (3, 15)", new { Key = KeyNameCache.GetAppKeyID("common_parent"), AppID = appId });
            }

            if (!available)
            {
                Log.WriteDebug(nameof(FreeLicense), $"Package {subId} (app {appId}) did not pass release check");
                return;
            }

            if (parentAppId > 0 && !LicenseList.OwnedApps.ContainsKey(parentAppId))
            {
                Log.WriteDebug(nameof(FreeLicense), $"Parent app {parentAppId} is not owned to get {appId}");
                return;
            }

            Log.WriteDebug(nameof(FreeLicense), $"Requesting apps in package {subId}");

            QueueRequest(subId, appId);
        }
        private async Task ProcessKey(string keyName, string displayName, string value, bool isJSON = false)
        {
            if (keyName.Length > 90)
            {
                Log.WriteError(nameof(SubProcessor), $"Key {keyName} for SubID {SubID} is too long, not inserting info.");

                return;
            }

            // All keys in PICS are supposed to be lower case.
            // But currently some keys in packages are not lowercased,
            // this lowercases everything to make sure nothing breaks in future
            keyName = keyName.ToLowerInvariant().Trim();

            if (!CurrentData.ContainsKey(keyName))
            {
                CurrentData[keyName] = new PICSInfo
                {
                    Processed = true,
                };

                var key = KeyNameCache.GetSubKeyID(keyName);

                if (key == 0)
                {
                    var type = isJSON ? 86 : 0; // 86 is a hardcoded const for the website

                    key = await KeyNameCache.CreateSubKey(keyName, displayName, type);

                    if (key == 0)
                    {
                        // We can't insert anything because key wasn't created
                        Log.WriteError(nameof(SubProcessor), $"Failed to create key {keyName} for SubID {SubID}, not inserting info.");

                        return;
                    }

                    IRC.Instance.SendOps($"New package keyname: {Colors.BLUE}{keyName} {Colors.LIGHTGRAY}(ID: {key}) ({displayName}) - {SteamDB.GetPackageUrl(SubID, "history")}");
                }

                await DbConnection.ExecuteAsync("INSERT INTO `SubsInfo` (`SubID`, `Key`, `Value`) VALUES (@SubID, @Key, @Value)", new { SubID, Key = key, Value = value });
                await MakeHistory("created_key", key, string.Empty, value);

                return;
            }

            var data = CurrentData[keyName];

            if (data.Processed)
            {
                Log.WriteWarn(nameof(SubProcessor), $"Duplicate key {keyName} in SubID {SubID}");

                return;
            }

            data.Processed = true;

            CurrentData[keyName] = data;

            if (data.Value == value)
            {
                return;
            }

            await DbConnection.ExecuteAsync("UPDATE `SubsInfo` SET `Value` = @Value WHERE `SubID` = @SubID AND `Key` = @Key", new { SubID, data.Key, Value = value });

            await MakeHistory("modified_key", data.Key, data.Value, value);
        }
Example #9
0
        public static async Task HandleMetadataInfo(SteamApps.PICSProductInfoCallback callback)
        {
            var apps = new List <uint>();
            var subs = new List <uint>();

            await using var db = await Database.GetConnectionAsync();

            if (callback.Apps.Any())
            {
                Log.WriteDebug(nameof(FullUpdateProcessor), $"Received metadata only product info for {callback.Apps.Count} apps ({callback.Apps.First().Key}...{callback.Apps.Last().Key}), job: {callback.JobID}");

                var currentChangeNumbers = (await db.QueryAsync <(uint, uint)>(
                                                "SELECT `AppID`, `Value` FROM `AppsInfo` WHERE `Key` = @ChangeNumberKey AND `AppID` IN @Apps",
                                                new
                {
                    ChangeNumberKey = KeyNameCache.GetAppKeyID("root_changenumber"),
                    Apps = callback.Apps.Keys
                }
                                                )).ToDictionary(x => x.Item1, x => x.Item2);

                foreach (var app in callback.Apps.Values)
                {
                    currentChangeNumbers.TryGetValue(app.ID, out var currentChangeNumber);

                    if (currentChangeNumber == app.ChangeNumber)
                    {
                        continue;
                    }

                    Log.WriteInfo(nameof(FullUpdateProcessor), $"App {app.ID} - Change: {currentChangeNumber} -> {app.ChangeNumber}");
                    apps.Add(app.ID);

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

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

            if (callback.Packages.Any())
            {
                Log.WriteDebug(nameof(FullUpdateProcessor), $"Received metadata only product info for {callback.Packages.Count} packages ({callback.Packages.First().Key}...{callback.Packages.Last().Key}), job: {callback.JobID}");

                var currentChangeNumbers = (await db.QueryAsync <(uint, uint)>(
                                                "SELECT `SubID`, `Value` FROM `SubsInfo` WHERE `Key` = @ChangeNumberKey AND `SubID` IN @Subs",
                                                new
                {
                    ChangeNumberKey = KeyNameCache.GetSubKeyID("root_changenumber"),
                    Subs = callback.Packages.Keys
                }
                                                )).ToDictionary(x => x.Item1, x => x.Item2);

                foreach (var sub in callback.Packages.Values)
                {
                    currentChangeNumbers.TryGetValue(sub.ID, out var currentChangeNumber);

                    if (currentChangeNumber == sub.ChangeNumber)
                    {
                        continue;
                    }

                    Log.WriteInfo(nameof(FullUpdateProcessor), $"Package {sub.ID} - Change: {currentChangeNumber} -> {sub.ChangeNumber}");
                    subs.Add(sub.ID);

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

                        await db.ExecuteAsync("INSERT INTO `ChangelistsSubs` (`ChangeID`, `SubID`) VALUES (@ChangeNumber, @SubID) ON DUPLICATE KEY UPDATE `SubID` = `SubID`", new { SubID = sub.ID, sub.ChangeNumber });
                    }
                }
            }

            if (apps.Any() || subs.Any())
            {
                JobManager.AddJob(
                    () => Steam.Instance.Apps.PICSGetAccessTokens(apps, subs),
                    new PICSTokens.RequestedTokens
                {
                    Apps     = apps,
                    Packages = subs,
                });
            }
        }
        private async Task <bool> ProcessKey(string keyName, string displayName, string value, KeyValue newKv = null)
        {
            if (keyName.Length > 90)
            {
                Log.WriteError("App Processor", "Key {0} for AppID {1} is too long, not inserting info.", keyName, AppID);

                return(false);
            }

            // All keys in PICS are supposed to be lower case
            keyName = keyName.ToLowerInvariant().Trim();

            if (!CurrentData.ContainsKey(keyName))
            {
                var key = KeyNameCache.GetAppKeyID(keyName);

                if (key == 0)
                {
                    var type = newKv != null ? 86 : 0; // 86 is a hardcoded const for the website

                    key = await KeyNameCache.CreateAppKey(keyName, displayName, type);

                    if (key == 0)
                    {
                        // We can't insert anything because key wasn't created
                        Log.WriteError("App Processor", "Failed to create key {0} for AppID {1}, not inserting info.", keyName, AppID);

                        return(false);
                    }

                    IRC.Instance.SendOps("New app keyname: {0}{1} {2}(ID: {3}) ({4}) - {5}", Colors.BLUE, keyName, Colors.LIGHTGRAY, key, displayName, SteamDB.GetAppURL(AppID, "history"));
                }

                await DbConnection.ExecuteAsync("INSERT INTO `AppsInfo` (`AppID`, `Key`, `Value`) VALUES (@AppID, @Key, @Value)", new { AppID, Key = key, Value = value });
                await MakeHistory("created_key", key, string.Empty, value);

                if ((keyName == "extended_developer" || keyName == "extended_publisher") && value == "Valve")
                {
                    IRC.Instance.SendOps("New {0}=Valve app: {1}{2}{3} -{4} {5}",
                                         displayName,
                                         Colors.BLUE, Steam.GetAppName(AppID), Colors.NORMAL,
                                         Colors.DARKBLUE, SteamDB.GetAppURL(AppID, "history"));
                }

                if (keyName == "common_oslist" && value.Contains("linux"))
                {
                    PrintLinux();
                }

                return(true);
            }

            var data = CurrentData[keyName];

            if (data.Processed)
            {
                Log.WriteWarn("App Processor", "Duplicate key {0} in AppID {1}", keyName, AppID);

                return(false);
            }

            data.Processed = true;

            CurrentData[keyName] = data;

            if (data.Value == value)
            {
                return(false);
            }

            await DbConnection.ExecuteAsync("UPDATE `AppsInfo` SET `Value` = @Value WHERE `AppID` = @AppID AND `Key` = @Key", new { AppID, data.Key, Value = value });

            if (newKv != null)
            {
                await MakeHistoryForJson(data.Key, data.Value, newKv);
            }
            else
            {
                await MakeHistory("modified_key", data.Key, data.Value, value);
            }

            if (keyName == "common_oslist" && value.Contains("linux") && !data.Value.Contains("linux"))
            {
                PrintLinux();
            }

            return(true);
        }
Example #11
0
        private async Task ProcessDlcNames(uint parentAppId, uint changeNumber, Dictionary <uint, string> dlcNames)
        {
            // TODO: Track name changes?
            await using var db = await Database.GetConnectionAsync();

            var dlcApps = await db.QueryAsync <App>("SELECT `AppID` FROM `Apps` WHERE `AppID` IN @Ids AND `LastKnownName` = \"\"", new { Ids = dlcNames.Keys });

            var parentKey = KeyNameCache.GetAppKeyID("common_parent");

            foreach (var dlcApp in dlcApps)
            {
                Log.WriteInfo(nameof(DepotProcessor), $"Got a name for app {dlcApp.AppID} from parent app {parentAppId}");

                var name = dlcNames[dlcApp.AppID];
                name = DepotNameStart.Replace(name, string.Empty);
                name = DepotNameEnd.Replace(name, string.Empty);

                var dlcAppIdEnding = $" ({dlcApp.AppID})";

                if (name.EndsWith(dlcAppIdEnding))
                {
                    name = name.Substring(0, name.Length - dlcAppIdEnding.Length);
                }

                name = name.Trim();

                await db.ExecuteAsync("UPDATE `Apps` SET `LastKnownName` = @AppName WHERE `AppID` = @AppID", new
                {
                    dlcApp.AppID,
                    AppName = name,
                });

                await db.ExecuteAsync("INSERT INTO `AppsInfo` (`AppID`, `Key`, `Value`) VALUES (@AppID, @Key, @Value) ON DUPLICATE KEY UPDATE `AppID` = `AppID`", new
                {
                    dlcApp.AppID,
                    Key   = parentKey,
                    Value = parentAppId.ToString(),
                });

                await db.ExecuteAsync(AppProcessor.HistoryQuery,
                                      new PICSHistory
                {
                    ID       = dlcApp.AppID,
                    ChangeID = changeNumber,
                    Key      = SteamDB.DatabaseNameType,
                    NewValue = name,
                    OldValue = "Depot name",
                    Action   = "created_info",
                }
                                      );

                await db.ExecuteAsync(AppProcessor.HistoryQuery,
                                      new PICSHistory
                {
                    ID       = dlcApp.AppID,
                    ChangeID = changeNumber,
                    Key      = parentKey,
                    NewValue = parentAppId.ToString(),
                    Action   = "created_key",
                }
                                      );
            }
        }