Exemplo n.º 1
0
        private void OnServiceMethod(SteamUnifiedMessages.ServiceMethodResponse callback)
        {
            JobAction job;

            if (!JobManager.TryRemoveJob(callback.JobID, out job) || !job.IsCommand)
            {
                return;
            }

            var request = job.CommandRequest;

            if (callback.Result != EResult.OK)
            {
                CommandHandler.ReplyToCommand(request.Command, "Unable to make service request: {0}", callback.Result);

                return;
            }

            // .chat about grate
            switch (request.Type)
            {
            case JobManager.IRCRequestType.TYPE_GAMESERVERS:
                ServersCommand.OnServiceMethod(callback, request);
                return;

            case JobManager.IRCRequestType.TYPE_PUBFILE:
            case JobManager.IRCRequestType.TYPE_PUBFILE_SILENT:
                PubFileCommand.OnServiceMethod(callback, request);
                return;
            }

            CommandHandler.ReplyToCommand(request.Command, "Unknown request type, I don't know what to do.");
        }
Exemplo n.º 2
0
        private void OnUGCInfo(SteamCloud.UGCDetailsCallback callback)
        {
            JobAction job;

            if (!JobManager.TryRemoveJob(callback.JobID, out job) || !job.IsCommand)
            {
                return;
            }

            var request = job.CommandRequest;

            if (callback.Result != EResult.OK)
            {
                CommandHandler.ReplyToCommand(request.Command, "Unable to request UGC info: {0}", callback.Result);

                return;
            }

            CommandHandler.ReplyToCommand(request.Command, "Creator: {0}{1}{2}, App: {3}{4}{5}, File: {6}{7}{8}, Size: {9}{10}{11} -{12} {13}",
                                          Colors.BLUE, callback.Creator.Render(true), Colors.NORMAL,
                                          Colors.BLUE, callback.AppID, Colors.NORMAL,
                                          Colors.BLUE, callback.FileName, Colors.NORMAL,
                                          Colors.BLUE, GetByteSizeString(callback.FileSize), Colors.NORMAL,
                                          Colors.DARKBLUE, callback.URL
                                          );
        }
        private void OnDepotKeyCallback(SteamApps.DepotKeyCallback callback)
        {
            JobAction job;

            if (!JobManager.TryRemoveJob(callback.JobID, out job))
            {
                RemoveLock(callback.DepotID);

                return;
            }

            var request = job.ManifestJob;

            if (callback.Result != EResult.OK)
            {
                if (callback.Result != EResult.AccessDenied || FileDownloader.IsImportantDepot(request.DepotID))
                {
                    Log.WriteError("Depot Processor", "Failed to get depot key for depot {0} (parent {1}) - {2}", callback.DepotID, request.ParentAppID, callback.Result);
                }

                RemoveLock(request.DepotID);

                return;
            }

            request.DepotKey = callback.DepotKey;
            request.Tries    = CDNServers.Count;
            request.Server   = GetContentServer();

            JobManager.AddJob(() => Steam.Instance.Apps.GetCDNAuthToken(request.DepotID, request.Server), request);
        }
Exemplo n.º 4
0
        private static void OnPersonaState(SteamFriends.PersonaStateCallback callback)
        {
            JobAction job;

            if (!JobManager.TryRemoveJob(new JobID(callback.FriendID), out job) || !job.IsCommand)
            {
                return;
            }

            var command = job.CommandRequest.Command;

            if (callback.FriendID.IsClanAccount)
            {
                var clantag = string.IsNullOrEmpty(callback.ClanTag) ? string.Empty : string.Format(" {0}(Clan tag: {1}{2}{3})",
                                                                                                    Colors.NORMAL, Colors.LIGHTGRAY, callback.ClanTag, Colors.NORMAL);

                CommandHandler.ReplyToCommand(command, "{0}{1}{2} -{3} https://steamcommunity.com/gid/{4}/{5}",
                                              Colors.BLUE, callback.Name, Colors.NORMAL,
                                              Colors.DARKBLUE, callback.FriendID.ConvertToUInt64(), clantag
                                              );
            }
            else if (callback.FriendID.IsIndividualAccount)
            {
                CommandHandler.ReplyToCommand(command, "{0}{1}{2} -{3} https://steamcommunity.com/profiles/{4}/ {5}(Last login: {6}, Last logoff: {7})",
                                              Colors.BLUE, callback.Name, Colors.NORMAL,
                                              Colors.DARKBLUE, callback.FriendID.ConvertToUInt64(),
                                              Colors.DARKGRAY, callback.LastLogOn, callback.LastLogOff
                                              );
            }
            else
            {
                CommandHandler.ReplyToCommand(command, callback.Name);
            }
        }
Exemplo n.º 5
0
        private static void OnNumberOfPlayers(SteamUserStats.NumberOfPlayersCallback callback)
        {
            JobAction job;

            if (!JobManager.TryRemoveJob(callback.JobID, out job) || !job.IsCommand)
            {
                return;
            }

            var request = job.CommandRequest;

            if (callback.Result != EResult.OK)
            {
                CommandHandler.ReplyToCommand(request.Command, "Unable to request player count: {0}{1}", Colors.RED, callback.Result);
            }
            else if (request.Target == 0)
            {
                CommandHandler.ReplyToCommand(
                    request.Command,
                    "{0}{1:N0}{2} people praising lord Gaben right now, influence:{3} {4}",
                    Colors.OLIVE, callback.NumPlayers, Colors.NORMAL,
                    Colors.DARKBLUE, SteamDB.GetAppURL(753, "graphs")
                    );
            }
            else
            {
                CommandHandler.ReplyToCommand(
                    request.Command,
                    "People playing {0}{1}{2} right now: {3}{4:N0}{5} -{6} {7}",
                    Colors.BLUE, Steam.GetAppName(request.Target), Colors.NORMAL,
                    Colors.OLIVE, callback.NumPlayers, Colors.NORMAL,
                    Colors.DARKBLUE, SteamDB.GetAppURL(request.Target, "graphs")
                    );
            }
        }
Exemplo n.º 6
0
        private static void OnPersonaState(SteamFriends.PersonaStateCallback callback)
        {
            if (!JobManager.TryRemoveJob(new JobID(callback.FriendID), out var job))
            {
                return;
            }

            var command = job.Command;

            if (callback.FriendID.IsClanAccount)
            {
                var clantag = string.IsNullOrEmpty(callback.ClanTag) ?
                              string.Empty :
                              $" {Colors.NORMAL}(Clan tag: {Colors.LIGHTGRAY}{callback.ClanTag}{Colors.NORMAL})";

                command.Reply($"{Colors.BLUE}{callback.Name}{Colors.NORMAL} -{Colors.DARKBLUE} https://steamcommunity.com/gid/{callback.FriendID.ConvertToUInt64()}/{clantag}");
            }
            else if (callback.FriendID.IsIndividualAccount)
            {
                command.Reply($"{Colors.BLUE}{callback.Name}{Colors.NORMAL} -{Colors.DARKBLUE} https://steamcommunity.com/profiles/{callback.FriendID.ConvertToUInt64()}/");
            }
            else
            {
                command.Reply(callback.Name);
            }
        }
Exemplo n.º 7
0
        private 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.Any())
            {
                JobManager.AddJob(() => Steam.Instance.Apps.PICSGetAccessTokens(appIDs, Enumerable.Empty <uint>()));
            }

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

                TaskManager.RunAsync(() =>
                {
                    RefreshPackageNames();

                    foreach (var subID in packageIDs)
                    {
                        IRC.Instance.SendAnnounce("New free license granted: {0}{1}{2} -{3} {4}",
                                                  Colors.BLUE, Steam.GetPackageName(subID), Colors.NORMAL,
                                                  Colors.DARKBLUE, SteamDB.GetPackageURL(subID)
                                                  );
                    }
                });
            }
        }
Exemplo n.º 8
0
        private static void OnPersonaState(SteamFriends.PersonaStateCallback callback)
        {
            if (!JobManager.TryRemoveJob(new JobID(callback.FriendID), out var job))
            {
                return;
            }

            var command = job.Command;

            if (callback.FriendID.IsClanAccount)
            {
                var clantag = string.IsNullOrEmpty(callback.ClanTag) ? string.Empty : string.Format(" {0}(Clan tag: {1}{2}{3})",
                                                                                                    Colors.NORMAL, Colors.LIGHTGRAY, callback.ClanTag, Colors.NORMAL);

                command.Reply("{0}{1}{2} -{3} https://steamcommunity.com/gid/{4}/{5}",
                              Colors.BLUE, callback.Name, Colors.NORMAL,
                              Colors.DARKBLUE, callback.FriendID.ConvertToUInt64(), clantag
                              );
            }
            else if (callback.FriendID.IsIndividualAccount)
            {
                command.Reply("{0}{1}{2} -{3} https://steamcommunity.com/profiles/{4}/",
                              Colors.BLUE, callback.Name, Colors.NORMAL,
                              Colors.DARKBLUE, callback.FriendID.ConvertToUInt64()
                              );
            }
            else
            {
                command.Reply(callback.Name);
            }
        }
Exemplo n.º 9
0
        private void OnFreeLicenseCallback(SteamApps.FreeLicenseCallback callback)
        {
            JobManager.TryRemoveJob(callback.JobID);

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

            Log.WriteDebug(nameof(FreeLicense), $"Received free license: {callback.Result} ({appIDs.Count} apps: {string.Join(", ", appIDs)}, {packageIDs.Count} packages: {string.Join(", ", packageIDs)})");

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

                var removed = false;

                foreach (var appid in appIDs)
                {
                    if (FreeLicensesToRequest.Remove(appid))
                    {
                        removed = true;
                    }
                }

                if (removed)
                {
                    TaskManager.Run(Save);
                }
            }

            if (packageIDs.Count > 0)
            {
                JobManager.AddJob(
                    () => Steam.Instance.Apps.PICSGetAccessTokens(Enumerable.Empty <uint>(), packageIDs),
                    new PICSTokens.RequestedTokens
                {
                    Packages = packageIDs.ToList()
                });

                TaskManager.Run(async() =>
                {
                    await RefreshPackageNames();

                    foreach (var subID in packageIDs)
                    {
                        IRC.Instance.SendAnnounce($"New free license granted: {Colors.BLUE}{Steam.GetPackageName(subID)}{Colors.NORMAL} -{Colors.DARKBLUE} {SteamDB.GetPackageUrl(subID)}");
                    }
                });
            }
        }
Exemplo n.º 10
0
        private void OnCDNAuthTokenCallback(SteamApps.CDNAuthTokenCallback callback)
        {
            JobAction job;

            if (!JobManager.TryRemoveJob(callback.JobID, out job))
            {
                return;
            }

            var request = job.ManifestJob;

            if (callback.Result != EResult.OK)
            {
                if (FileDownloader.IsImportantDepot(request.DepotID))
                {
                    Log.WriteError("Depot Processor", "Failed to get CDN auth token for depot {0} (parent {1} - server {2}) - {3} (#{4})",
                                   request.DepotID, request.ParentAppID, request.Server, callback.Result, request.Tries);
                }

                if (--request.Tries >= 0)
                {
                    request.Server = GetContentServer(request.Tries);

                    JobManager.AddJob(() => Steam.Instance.Apps.GetCDNAuthToken(request.DepotID, request.Server), request);

                    return;
                }

                RemoveLock(request.DepotID);

                return;
            }

            request.CDNToken = callback.Token;

            // TODO: Using tasks makes every manifest download timeout
            // TODO: which seems to be bug with mono's threadpool implementation

            /*TaskManager.Run(() => DownloadManifest(request)).ContinueWith(task =>
             * {
             *  RemoveLock(request.DepotID);
             *
             *  Log.WriteDebug("Depot Processor", "Processed depot {0} ({1} depot locks left)", request.DepotID, DepotLocks.Count);
             * });*/

            try
            {
                DownloadManifest(request);
            }
            catch (Exception)
            {
                RemoveLock(request.DepotID);
            }
        }
Exemplo n.º 11
0
        private static void OnPICSTokens(SteamApps.PICSTokensCallback callback)
        {
            JobManager.TryRemoveJob(callback.JobID, out var job);

            if (callback.AppTokens.Count > 0 || callback.AppTokensDenied.Count > 0)
            {
                Log.WriteInfo(nameof(PICSTokens), $"App tokens: {callback.AppTokens.Count} received, {callback.AppTokensDenied.Count} denied");
            }
            else
            {
                Log.WriteInfo(nameof(PICSTokens), $"Package tokens: {callback.PackageTokens.Count} received, {callback.PackageTokensDenied.Count} denied");
            }

            var apps = callback.AppTokensDenied
                       .Select(NewAppRequest)
                       .Concat(callback.AppTokens.Select(app => NewAppRequest(app.Key, app.Value)))
                       .ToList();

            var subs = callback.PackageTokensDenied
                       .Select(NewPackageRequest)
                       .Concat(callback.PackageTokens.Select(sub => NewPackageRequest(sub.Key, sub.Value)))
                       .ToList();

            if (job?.Metadata != default)
            {
                var requested = (RequestedTokens)job.Metadata;

                if (requested.Apps != null)
                {
                    foreach (var appid in requested.Apps.Where(app =>
                                                               !callback.AppTokens.ContainsKey(app) && !callback.AppTokensDenied.Contains(app)))
                    {
                        Log.WriteError(nameof(PICSTokens), $"Requested token for app {appid} but Steam did not return it");
                        IRC.Instance.SendOps($"[TOKENS] Requested token for app {appid} but Steam did not return it");

                        apps.Add(NewAppRequest(appid));
                    }
                }

                if (requested.Packages != null)
                {
                    foreach (var subid in requested.Packages.Where(sub =>
                                                                   !callback.PackageTokens.ContainsKey(sub) && !callback.PackageTokensDenied.Contains(sub)))
                    {
                        Log.WriteError(nameof(PICSTokens), $"Requested token for package {subid} but Steam did not return it");

                        subs.Add(NewPackageRequest(subid));
                    }
                }
            }

            JobManager.AddJob(() => Steam.Instance.Apps.PICSGetProductInfo(apps, subs));
        }
Exemplo n.º 12
0
        private static void OnPICSTokens(SteamApps.PICSTokensCallback callback)
        {
            JobManager.TryRemoveJob(callback.JobID);

            Log.WriteDebug("PICSTokens", "Tokens granted: {0} - Tokens denied: {1}", callback.AppTokens.Count, callback.AppTokensDenied.Count);

            var apps = callback.AppTokensDenied
                       .Select(Utils.NewPICSRequest)
                       .Concat(callback.AppTokens.Select(app => Utils.NewPICSRequest(app.Key, app.Value)));

            JobManager.AddJob(() => Steam.Instance.Apps.PICSGetProductInfo(apps, Enumerable.Empty <SteamApps.PICSRequest>()));
        }
Exemplo n.º 13
0
        private void OnDepotKeyCallback(SteamApps.DepotKeyCallback callback)
        {
            JobAction job;

            if (!JobManager.TryRemoveJob(callback.JobID, out job))
            {
                RemoveLock(callback.DepotID);

                return;
            }

            var request = job.ManifestJob;

            if (callback.Result != EResult.OK)
            {
                if (callback.Result != EResult.AccessDenied || FileDownloader.IsImportantDepot(request.DepotID))
                {
                    Log.WriteError("Depot Processor", "Failed to get depot key for depot {0} (parent {1}) - {2}", callback.DepotID, request.ParentAppID, callback.Result);
                }

                RemoveLock(request.DepotID);

                return;
            }

            request.DepotKey = callback.DepotKey;
            request.Tries    = CDNServers.Count;
            request.Server   = GetContentServer();

            JobManager.AddJob(() => Steam.Instance.Apps.GetCDNAuthToken(request.DepotID, request.Server), request);

            var decryptionKey = Utils.ByteArrayToString(callback.DepotKey);

            using (var db = Database.GetConnection())
            {
                var currentDecryptionKey = db.ExecuteScalar <string>("SELECT `Key` FROM `DepotsKeys` WHERE `DepotID` = @DepotID", new { callback.DepotID });

                if (decryptionKey != currentDecryptionKey)
                {
                    if (currentDecryptionKey != null)
                    {
                        Log.WriteInfo("Depot Processor", "Decryption key for {0} changed: {1} -> {2}", callback.DepotID, currentDecryptionKey, decryptionKey);

                        IRC.Instance.SendOps("Decryption key for {0} changed: {1} -> {2}", callback.DepotID, currentDecryptionKey, decryptionKey);
                    }

                    db.Execute("INSERT INTO `DepotsKeys` (`DepotID`, `Key`) VALUES (@DepotID, @Key) ON DUPLICATE KEY UPDATE `Key` = VALUES(`Key`)", new { callback.DepotID, Key = decryptionKey });
                }
            }
        }
Exemplo n.º 14
0
        private void OnFreeLicenseCallback(SteamApps.FreeLicenseCallback callback)
        {
            JobManager.TryRemoveJob(callback.JobID);

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

            Log.WriteDebug(nameof(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>()),
                    new PICSTokens.RequestedTokens
                {
                    Apps = appIDs.ToList()
                });

                foreach (var appid in appIDs)
                {
                    LocalConfig.Current.FreeLicensesToRequest.Remove(appid);
                }

                LocalConfig.Save();
            }

            if (packageIDs.Count > 0)
            {
                JobManager.AddJob(
                    () => Steam.Instance.Apps.PICSGetAccessTokens(Enumerable.Empty <uint>(), packageIDs),
                    new PICSTokens.RequestedTokens
                {
                    Packages = packageIDs.ToList()
                });

                TaskManager.RunAsync(async() =>
                {
                    await RefreshPackageNames();

                    foreach (var subID in packageIDs)
                    {
                        IRC.Instance.SendAnnounce("New free license granted: {0}{1}{2} -{3} {4}",
                                                  Colors.BLUE, Steam.GetPackageName(subID), Colors.NORMAL,
                                                  Colors.DARKBLUE, SteamDB.GetPackageUrl(subID)
                                                  );
                    }
                });
            }
        }
Exemplo n.º 15
0
        private static void OnPICSTokens(SteamApps.PICSTokensCallback callback)
        {
            JobManager.TryRemoveJob(callback.JobID);

            Log.WriteDebug(nameof(PICSTokens), $"App tokens: {callback.AppTokens.Count} ({callback.AppTokensDenied.Count} denied) - Package tokens: {callback.PackageTokens.Count} ({callback.PackageTokensDenied.Count} denied)");

            var apps = callback.AppTokensDenied
                       .Select(NewAppRequest)
                       .Concat(callback.AppTokens.Select(app => NewAppRequest(app.Key, app.Value)));

            var subs = callback.PackageTokensDenied
                       .Select(NewPackageRequest)
                       .Concat(callback.PackageTokens.Select(sub => NewPackageRequest(sub.Key, sub.Value)));

            JobManager.AddJob(() => Steam.Instance.Apps.PICSGetProductInfo(apps, subs));
        }
Exemplo n.º 16
0
        private void OnFreeLicenseCallback(SteamApps.FreeLicenseCallback callback)
        {
            JobManager.TryRemoveJob(callback.JobID);

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

            Log.WriteDebug(nameof(FreeLicense), $"Received free license: {callback.Result} ({appIDs.Count} apps: {string.Join(", ", appIDs)}, {packageIDs.Count} packages: {string.Join(", ", packageIDs)})");

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

                var removed = false;

                foreach (var appid in appIDs)
                {
                    if (FreeLicensesToRequest.TryRemove(appid, out _))
                    {
                        removed = true;
                    }
                }

                if (removed)
                {
                    TaskManager.Run(Save);
                }
            }

            if (packageIDs.Count > 0)
            {
                JobManager.AddJob(
                    () => Steam.Instance.Apps.PICSGetAccessTokens(Enumerable.Empty <uint>(), packageIDs),
                    new PICSTokens.RequestedTokens
                {
                    Packages = packageIDs.ToList()
                });

                TaskManager.Run(RefreshPackageNames);
            }
        }
Exemplo n.º 17
0
        private static void OnPICSTokens(SteamApps.PICSTokensCallback callback)
        {
            Log.WriteDebug("Steam", "Tokens granted: {0} - Tokens denied: {1}", callback.AppTokens.Count, callback.AppTokensDenied.Count);

            var apps = callback.AppTokensDenied
                       .Select(app => Utils.NewPICSRequest(app))
                       .Concat(callback.AppTokens.Select(app => Utils.NewPICSRequest(app.Key, app.Value)));

            Func <JobID> func = () => Steam.Instance.Apps.PICSGetProductInfo(apps, Enumerable.Empty <SteamApps.PICSRequest>());

            JobAction job;

            // We have to preserve CommandRequest between jobs
            if (JobManager.TryRemoveJob(callback.JobID, out job) && job.IsCommand)
            {
                JobManager.AddJob(func, job.CommandRequest);

                return;
            }

            JobManager.AddJob(func);
        }
Exemplo n.º 18
0
        private static void OnPICSProductInfo(SteamApps.PICSProductInfoCallback callback)
        {
            JobManager.TryRemoveJob(callback.JobID);

            var apps     = callback.Apps.Concat(callback.UnknownApps.ToDictionary(x => x, x => (SteamApps.PICSProductInfoCallback.PICSProductInfo)null));
            var packages = callback.Packages.Concat(callback.UnknownPackages.ToDictionary(x => x, x => (SteamApps.PICSProductInfoCallback.PICSProductInfo)null));

            foreach (var workaround in apps)
            {
                var app = workaround;

                Log.WriteInfo("PICSProductInfo", "{0}AppID: {1}", app.Value == null ? "Unknown " : "", app.Key);

                Task mostRecentItem;

                lock (ProcessedApps)
                {
                    ProcessedApps.TryGetValue(app.Key, out mostRecentItem);
                }

                var workerItem = TaskManager.Run(async() =>
                {
                    try
                    {
                        await ProcessorSemaphore.WaitAsync().ConfigureAwait(false);

                        if (mostRecentItem != null && !mostRecentItem.IsCompleted)
                        {
                            Log.WriteDebug("PICSProductInfo", "Waiting for app {0} to finish processing", app.Key);

                            await mostRecentItem.ConfigureAwait(false);
                        }

                        using (var processor = new AppProcessor(app.Key))
                        {
                            if (app.Value == null)
                            {
                                processor.ProcessUnknown();
                            }
                            else
                            {
                                processor.Process(app.Value);
                            }
                        }
                    }
                    catch (MySqlException e)
                    {
                        ErrorReporter.Notify($"App {app.Key}", e);

                        JobManager.AddJob(() => Steam.Instance.Apps.PICSGetAccessTokens(app.Key, null));
                    }
                    catch (Exception e)
                    {
                        ErrorReporter.Notify($"App {app.Key}", e);
                    }
                    finally
                    {
                        lock (ProcessedApps)
                        {
                            if (ProcessedApps.TryGetValue(app.Key, out mostRecentItem) && mostRecentItem.IsCompleted)
                            {
                                ProcessedApps.Remove(app.Key);
                            }
                        }

                        ProcessorSemaphore.Release();
                    }
                });

                if (Settings.IsFullRun)
                {
                    continue;
                }

                lock (ProcessedApps)
                {
                    ProcessedApps[app.Key] = workerItem;
                }
            }

            foreach (var workaround in packages)
            {
                var package = workaround;

                Log.WriteInfo("PICSProductInfo", "{0}SubID: {1}", package.Value == null ? "Unknown " : "", package.Key);

                Task mostRecentItem;

                lock (ProcessedSubs)
                {
                    ProcessedSubs.TryGetValue(package.Key, out mostRecentItem);
                }

                var workerItem = TaskManager.Run(async() =>
                {
                    try
                    {
                        await ProcessorSemaphore.WaitAsync().ConfigureAwait(false);

                        if (mostRecentItem != null && !mostRecentItem.IsCompleted)
                        {
                            Log.WriteDebug("PICSProductInfo", "Waiting for package {0} to finish processing", package.Key);

                            await mostRecentItem.ConfigureAwait(false);
                        }

                        using (var processor = new SubProcessor(package.Key))
                        {
                            if (package.Value == null)
                            {
                                processor.ProcessUnknown();
                            }
                            else
                            {
                                processor.Process(package.Value);
                            }
                        }
                    }
                    catch (MySqlException e)
                    {
                        ErrorReporter.Notify($"Package {package.Key}", e);

                        JobManager.AddJob(() => Steam.Instance.Apps.PICSGetProductInfo(null, package.Key, false, false));
                    }
                    catch (Exception e)
                    {
                        ErrorReporter.Notify($"Package {package.Key}", e);
                    }
                    finally
                    {
                        lock (ProcessedSubs)
                        {
                            if (ProcessedSubs.TryGetValue(package.Key, out mostRecentItem) && mostRecentItem.IsCompleted)
                            {
                                ProcessedSubs.Remove(package.Key);
                            }
                        }

                        ProcessorSemaphore.Release();
                    }
                });

                if (Settings.IsFullRun)
                {
                    continue;
                }

                lock (ProcessedSubs)
                {
                    ProcessedSubs[package.Key] = workerItem;
                }
            }
        }
Exemplo n.º 19
0
        private static void OnPICSProductInfo(SteamApps.PICSProductInfoCallback callback)
        {
            JobManager.TryRemoveJob(callback.JobID);

            var apps     = callback.Apps.Concat(callback.UnknownApps.ToDictionary(x => x, x => (SteamApps.PICSProductInfoCallback.PICSProductInfo)null));
            var packages = callback.Packages.Concat(callback.UnknownPackages.ToDictionary(x => x, x => (SteamApps.PICSProductInfoCallback.PICSProductInfo)null));

            foreach (var workaround in apps)
            {
                var app = workaround;

                Log.WriteInfo("PICSProductInfo", "{0}AppID: {1}", app.Value == null ? "Unknown " : "", app.Key);

                IWorkItemResult mostRecentItem;

                lock (ProcessedApps)
                {
                    ProcessedApps.TryGetValue(app.Key, out mostRecentItem);
                }

                var workerItem = ProcessorThreadPool.QueueWorkItem(delegate
                {
                    try
                    {
                        if (mostRecentItem != null && !mostRecentItem.IsCompleted)
                        {
                            Log.WriteDebug("PICSProductInfo", "Waiting for app {0} to finish processing", app.Key);

                            SmartThreadPool.WaitAll(new IWaitableResult[] { mostRecentItem });
                        }

                        using (var processor = new AppProcessor(app.Key))
                        {
                            if (app.Value == null)
                            {
                                processor.ProcessUnknown();
                            }
                            else
                            {
                                processor.Process(app.Value);
                            }
                        }
                    }
                    catch (MySqlException e)
                    {
                        Log.WriteError("PICSProductInfo", "App {0} faulted: {1}", app.Key, e);

                        JobManager.AddJob(() => Steam.Instance.Apps.PICSGetAccessTokens(app.Key, null));
                    }
                    catch (Exception e)
                    {
                        Log.WriteError("PICSProductInfo", "App {0} faulted: {1}", app.Key, e);
                    }
                    finally
                    {
                        lock (ProcessedApps)
                        {
                            if (ProcessedApps.TryGetValue(app.Key, out mostRecentItem) && mostRecentItem.IsCompleted)
                            {
                                ProcessedApps.Remove(app.Key);
                            }
                        }
                    }
                });

                if (Settings.IsFullRun)
                {
                    continue;
                }

                lock (ProcessedApps)
                {
                    ProcessedApps[app.Key] = workerItem;
                }
            }

            foreach (var workaround in packages)
            {
                var package = workaround;

                Log.WriteInfo("PICSProductInfo", "{0}SubID: {1}", package.Value == null ? "Unknown " : "", package.Key);

                IWorkItemResult mostRecentItem;

                lock (ProcessedSubs)
                {
                    ProcessedSubs.TryGetValue(package.Key, out mostRecentItem);
                }

                var workerItem = ProcessorThreadPool.QueueWorkItem(delegate
                {
                    try
                    {
                        if (mostRecentItem != null && !mostRecentItem.IsCompleted)
                        {
                            Log.WriteDebug("PICSProductInfo", "Waiting for package {0} to finish processing", package.Key);

                            SmartThreadPool.WaitAll(new IWaitableResult[] { mostRecentItem });
                        }

                        using (var processor = new SubProcessor(package.Key))
                        {
                            if (package.Value == null)
                            {
                                processor.ProcessUnknown();
                            }
                            else
                            {
                                processor.Process(package.Value);
                            }
                        }
                    }
                    catch (MySqlException e)
                    {
                        Log.WriteError("PICSProductInfo", "Package {0} faulted: {1}", package.Key, e);

                        JobManager.AddJob(() => Steam.Instance.Apps.PICSGetProductInfo(null, package.Key, false, false));
                    }
                    catch (Exception e)
                    {
                        Log.WriteError("PICSProductInfo", "Package {0} faulted: {1}", package.Key, e);
                    }
                    finally
                    {
                        lock (ProcessedSubs)
                        {
                            if (ProcessedSubs.TryGetValue(package.Key, out mostRecentItem) && mostRecentItem.IsCompleted)
                            {
                                ProcessedSubs.Remove(package.Key);
                            }
                        }
                    }
                });

                if (Settings.IsFullRun)
                {
                    continue;
                }

                lock (ProcessedSubs)
                {
                    ProcessedSubs[package.Key] = workerItem;
                }
            }
        }
Exemplo n.º 20
0
        private static void OnPICSProductInfo(SteamApps.PICSProductInfoCallback callback)
        {
            JobManager.TryRemoveJob(callback.JobID);

            var processors = new List <BaseProcessor>(
                callback.Apps.Count +
                callback.Packages.Count +
                callback.UnknownApps.Count +
                callback.UnknownPackages.Count
                );

            processors.AddRange(callback.Apps.Select(app => new AppProcessor(app.Key, app.Value)));
            processors.AddRange(callback.Packages.Select(package => new SubProcessor(package.Key, package.Value)));
            processors.AddRange(callback.UnknownApps.Select(app => new AppProcessor(app, null)));
            processors.AddRange(callback.UnknownPackages.Select(package => new SubProcessor(package, null)));

            foreach (var workaround in processors)
            {
                var processor = workaround;

                Task mostRecentItem;

                lock (CurrentlyProcessing)
                {
                    CurrentlyProcessing.TryGetValue(processor.Id, out mostRecentItem);
                }

                var workerItem = TaskManager.Run(async() =>
                {
                    await Semaphore.WaitAsync(TaskManager.TaskCancellationToken.Token).ConfigureAwait(false);

                    try
                    {
                        if (mostRecentItem != null && !mostRecentItem.IsCompleted)
                        {
                            Log.WriteDebug("PICSProductInfo", "Waiting for {0} to finish processing", processor.ToString());

                            await mostRecentItem.ConfigureAwait(false);
                        }

                        await processor.Process().ConfigureAwait(false);
                    }
                    finally
                    {
                        Semaphore.Release();

                        processor.Dispose();
                    }
                }).Unwrap();

                lock (CurrentlyProcessing)
                {
                    CurrentlyProcessing[processor.Id] = workerItem;
                }

                workerItem.ContinueWith(task =>
                {
                    lock (CurrentlyProcessing)
                    {
                        if (CurrentlyProcessing.TryGetValue(processor.Id, out mostRecentItem) && mostRecentItem.IsCompleted)
                        {
                            CurrentlyProcessing.Remove(processor.Id);
                        }
                    }
                }, TaskManager.TaskCancellationToken.Token);
            }
        }
Exemplo n.º 21
0
        public override void OnCommand(CommandArguments command)
        {
            if (command.Message.Length == 0)
            {
                CommandHandler.ReplyToCommand(command, "Usage:{0} steamid <steamid> [individual/group/gamegroup]", Colors.OLIVE);

                return;
            }

            var args    = command.Message.Split(' ');
            var urlType = EVanityURLType.Default;

            if (args.Length > 1)
            {
                switch (args[1])
                {
                case "individual":
                    urlType = EVanityURLType.Individual;
                    break;

                case "group":
                    urlType = EVanityURLType.Group;
                    break;

                case "game":
                case "gamegroup":
                    urlType = EVanityURLType.OfficialGameGroup;
                    break;

                default:
                    CommandHandler.ReplyToCommand(command, "Invalid vanity url type.");
                    return;
                }
            }

            SteamID steamID;

            if (urlType != EVanityURLType.Default || !TrySetSteamID(args[0], out steamID))
            {
                if (urlType == EVanityURLType.Default)
                {
                    urlType = EVanityURLType.Individual;
                }

                var eResult = ResolveVanityURL(args[0], urlType, out steamID);

                if (eResult != EResult.OK)
                {
                    CommandHandler.ReplyToCommand(command, "Failed to resolve vanity url: {0}{1}", Colors.RED, eResult.ToString());

                    return;
                }
            }

            CommandHandler.ReplyToCommand(command, ExpandSteamID(steamID));

            if (!steamID.IsValid || (!steamID.IsIndividualAccount && !steamID.IsClanAccount))
            {
                return;
            }

            JobAction job;

            if (JobManager.TryRemoveJob(new JobID(steamID), out job) && job.IsCommand)
            {
                CommandHandler.ReplyToCommand(job.CommandRequest.Command, true, "Your !steamid request was lost in space.");
            }

            JobManager.AddJob(
                () => FakePersonaStateJob(steamID),
                new JobManager.IRCRequest
            {
                Command = command
            }
                );
        }
Exemplo n.º 22
0
        private static void OnPICSProductInfo(SteamApps.PICSProductInfoCallback callback)
        {
            JobManager.TryRemoveJob(callback.JobID);

            var processors = new List <BaseProcessor>(
                callback.Apps.Count +
                callback.Packages.Count +
                callback.UnknownApps.Count +
                callback.UnknownPackages.Count
                );

            processors.AddRange(callback.Apps.Select(app => new AppProcessor(app.Key, app.Value)));
            processors.AddRange(callback.Packages.Select(package => new SubProcessor(package.Key, package.Value)));
            processors.AddRange(callback.UnknownApps.Select(app => new AppProcessor(app, null)));
            processors.AddRange(callback.UnknownPackages.Select(package => new SubProcessor(package, null)));

            foreach (var workaround in processors)
            {
                var processor = workaround;

                Task mostRecentItem;

                lock (CurrentlyProcessing)
                {
                    CurrentlyProcessing.TryGetValue(processor.Id, out mostRecentItem);
                }

                var workerItem = TaskManager.Run(async() =>
                {
                    try
                    {
                        await Semaphore.WaitAsync(TaskManager.TaskCancellationToken.Token).ConfigureAwait(false);

                        if (mostRecentItem?.IsCompleted == false)
                        {
                            Log.WriteDebug(processor.ToString(), $"Waiting for previous task to finish processing ({CurrentlyProcessing.Count})");

                            await mostRecentItem.ConfigureAwait(false);

#if DEBUG
                            Log.WriteDebug(processor.ToString(), "Previous task lock ended");
#endif
                        }

                        await processor.Process().ConfigureAwait(false);
                    }
                    catch (Exception e)
                    {
                        ErrorReporter.Notify(processor.ToString(), e);
                    }
                    finally
                    {
                        Semaphore.Release();

                        processor.Dispose();
                    }

                    return(processor);
                }).Unwrap();

                lock (CurrentlyProcessing)
                {
                    CurrentlyProcessing[processor.Id] = workerItem;
                }

                // Register error handler on inner task and the continuation
                TaskManager.RegisterErrorHandler(workerItem);
                TaskManager.RegisterErrorHandler(workerItem.ContinueWith(RemoveProcessorLock, TaskManager.TaskCancellationToken.Token));
            }
        }
Exemplo n.º 23
0
        private static void OnPICSProductInfo(SteamApps.PICSProductInfoCallback callback)
        {
            JobManager.TryRemoveJob(callback.JobID);

            var apps     = callback.Apps.Concat(callback.UnknownApps.ToDictionary(x => x, x => (SteamApps.PICSProductInfoCallback.PICSProductInfo)null));
            var packages = callback.Packages.Concat(callback.UnknownPackages.ToDictionary(x => x, x => (SteamApps.PICSProductInfoCallback.PICSProductInfo)null));

            foreach (var workaround in apps)
            {
                var app = workaround;

                Log.WriteInfo("PICSProductInfo", "{0}AppID: {1}", app.Value == null ? "Unknown " : "", app.Key);

                Task mostRecentItem;

                lock (ProcessedApps)
                {
                    ProcessedApps.TryGetValue(app.Key, out mostRecentItem);
                }

                var workerItem = TaskManager.Run(async delegate
                {
                    if (mostRecentItem != null && !mostRecentItem.IsCompleted)
                    {
                        Log.WriteDebug("PICSProductInfo", "Waiting for app {0} to finish processing", app.Key);

                        await mostRecentItem;
                    }

                    using (var processor = new AppProcessor(app.Key))
                    {
                        if (app.Value == null)
                        {
                            processor.ProcessUnknown();
                        }
                        else
                        {
                            processor.Process(app.Value);
                        }
                    }
                });

                if (Settings.IsFullRun)
                {
                    continue;
                }


                lock (ProcessedApps)
                {
                    ProcessedApps[app.Key] = workerItem;
                }

                workerItem.ContinueWith(task =>
                {
                    lock (ProcessedApps)
                    {
                        if (ProcessedApps.TryGetValue(app.Key, out mostRecentItem) && mostRecentItem.IsCompleted)
                        {
                            ProcessedApps.Remove(app.Key);
                        }
                    }
                });
            }

            foreach (var workaround in packages)
            {
                var package = workaround;

                Log.WriteInfo("PICSProductInfo", "{0}SubID: {1}", package.Value == null ? "Unknown " : "", package.Key);

                Task mostRecentItem;

                lock (ProcessedSubs)
                {
                    ProcessedSubs.TryGetValue(package.Key, out mostRecentItem);
                }

                var workerItem = TaskManager.Run(async delegate
                {
                    if (mostRecentItem != null && !mostRecentItem.IsCompleted)
                    {
                        Log.WriteDebug("PICSProductInfo", "Waiting for package {0} to finish processing", package.Key);

                        await mostRecentItem;
                    }

                    using (var processor = new SubProcessor(package.Key))
                    {
                        if (package.Value == null)
                        {
                            processor.ProcessUnknown();
                        }
                        else
                        {
                            processor.Process(package.Value);
                        }
                    }
                });

                if (Settings.IsFullRun)
                {
                    continue;
                }

                lock (ProcessedSubs)
                {
                    ProcessedSubs[package.Key] = workerItem;
                }

                workerItem.ContinueWith(task =>
                {
                    lock (ProcessedSubs)
                    {
                        if (ProcessedSubs.TryGetValue(package.Key, out mostRecentItem) && mostRecentItem.IsCompleted)
                        {
                            ProcessedSubs.Remove(package.Key);
                        }
                    }
                });
            }
        }
Exemplo n.º 24
0
        public override async Task OnCommand(CommandArguments command)
        {
            await Task.Yield();

            if (command.Message.Length == 0)
            {
                command.Reply("Usage:{0} steamid <steamid> [individual/group/gamegroup]", Colors.OLIVE);

                return;
            }

            var args    = command.Message.Split(' ');
            var urlType = EVanityURLType.Default;

            if (args.Length > 1)
            {
                switch (args[1])
                {
                case "individual":
                    urlType = EVanityURLType.Individual;
                    break;

                case "group":
                    urlType = EVanityURLType.Group;
                    break;

                case "game":
                case "gamegroup":
                    urlType = EVanityURLType.OfficialGameGroup;
                    break;

                default:
                    command.Reply("Invalid vanity url type.");
                    return;
                }
            }

            SteamID steamID;

            if (urlType != EVanityURLType.Default || !TrySetSteamID(args[0], out steamID))
            {
                if (urlType == EVanityURLType.Default)
                {
                    urlType = EVanityURLType.Individual;
                }

                var eResult = ResolveVanityURL(args[0], urlType, out steamID);

                if (eResult != EResult.OK)
                {
                    command.Reply("Failed to resolve vanity url: {0}{1}", Colors.RED, eResult.ToString());

                    return;
                }
            }

            command.Reply(ExpandSteamID(steamID));

            if (!steamID.IsValid || (!steamID.IsIndividualAccount && !steamID.IsClanAccount))
            {
                return;
            }

            JobManager.TryRemoveJob(new JobID(steamID)); // Remove previous "job" if any

            JobManager.AddJob(
                () => FakePersonaStateJob(steamID),
                command
                );
        }
Exemplo n.º 25
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)
                                                  );
                        }
                    }
                });
            }
        }
Exemplo n.º 26
0
        private static void OnPICSProductInfo(SteamApps.PICSProductInfoCallback callback)
        {
            JobAction job;

            if (!JobManager.TryRemoveJob(callback.JobID, out job) || !job.IsCommand)
            {
                return;
            }

            var request = job.CommandRequest;

            if (request.Type == JobManager.IRCRequestType.TYPE_SUB)
            {
                if (!callback.Packages.ContainsKey(request.Target))
                {
                    CommandHandler.ReplyToCommand(request.Command, "Unknown SubID: {0}{1}{2}", Colors.BLUE, request.Target, LicenseList.OwnedSubs.ContainsKey(request.Target) ? SteamDB.StringCheckmark : string.Empty);

                    return;
                }

                var    info = callback.Packages[request.Target];
                var    kv   = info.KeyValues.Children.FirstOrDefault();
                string name;

                if (kv["name"].Value != null)
                {
                    name = Utils.RemoveControlCharacters(kv["name"].AsString());
                }
                else
                {
                    name = Steam.GetPackageName(info.ID);
                }

                try
                {
                    kv.SaveToFile(Path.Combine(Application.Path, "sub", string.Format("{0}.vdf", info.ID)), false);
                }
                catch (Exception e)
                {
                    CommandHandler.ReplyToCommand(request.Command, "Unable to save file for {0}: {1}", name, e.Message);

                    return;
                }

                CommandHandler.ReplyToCommand(request.Command, "{0}{1}{2} -{3} {4}{5} - Dump:{6} {7}{8}{9}{10}",
                                              Colors.BLUE, name, Colors.NORMAL,
                                              Colors.DARKBLUE, SteamDB.GetPackageURL(info.ID), Colors.NORMAL,
                                              Colors.DARKBLUE, SteamDB.GetRawPackageURL(info.ID), Colors.NORMAL,
                                              info.MissingToken ? SteamDB.StringNeedToken : string.Empty,
                                              LicenseList.OwnedSubs.ContainsKey(info.ID) ? SteamDB.StringCheckmark : string.Empty
                                              );
            }
            else if (request.Type == JobManager.IRCRequestType.TYPE_APP)
            {
                if (!callback.Apps.ContainsKey(request.Target))
                {
                    CommandHandler.ReplyToCommand(request.Command, "Unknown AppID: {0}{1}{2}", Colors.BLUE, request.Target, LicenseList.OwnedApps.ContainsKey(request.Target) ? SteamDB.StringCheckmark : string.Empty);

                    return;
                }

                var    info = callback.Apps[request.Target];
                string name;

                if (info.KeyValues["common"]["name"].Value != null)
                {
                    name = Utils.RemoveControlCharacters(info.KeyValues["common"]["name"].AsString());
                }
                else
                {
                    name = Steam.GetAppName(info.ID);
                }

                try
                {
                    info.KeyValues.SaveToFile(Path.Combine(Application.Path, "app", string.Format("{0}.vdf", info.ID)), false);
                }
                catch (Exception e)
                {
                    CommandHandler.ReplyToCommand(request.Command, "Unable to save file for {0}: {1}", name, e.Message);

                    return;
                }

                CommandHandler.ReplyToCommand(request.Command, "{0}{1}{2} -{3} {4}{5} - Dump:{6} {7}{8}{9}{10}",
                                              Colors.BLUE, name, Colors.NORMAL,
                                              Colors.DARKBLUE, SteamDB.GetAppURL(info.ID), Colors.NORMAL,
                                              Colors.DARKBLUE, SteamDB.GetRawAppURL(info.ID), Colors.NORMAL,
                                              info.MissingToken ? SteamDB.StringNeedToken : string.Empty,
                                              LicenseList.OwnedApps.ContainsKey(info.ID) ? SteamDB.StringCheckmark : string.Empty
                                              );
            }
            else
            {
                CommandHandler.ReplyToCommand(request.Command, "I have no idea what happened here!");
            }
        }