Пример #1
0
        private void OnLicenseListCallback(SteamApps.LicenseListCallback licenseList)
        {
            if (licenseList.Result != EResult.OK)
            {
                Log.WriteError("Steam", "Unable to get license list: {0}", licenseList.Result);

                return;
            }

            Log.WriteInfo("Steam", "{0} Licenses: {1}", licenseList.LicenseList.Count, string.Join(", ", licenseList.LicenseList.Select(lic => lic.PackageID)));

            var timeNow = DateTime.Now;

            foreach (var license in licenseList.LicenseList)
            {
                if (license.PackageID != 0 && !OwnedPackages.Contains(license.PackageID) && timeNow.Subtract(license.TimeCreated).TotalSeconds < 600)
                {
                    IRC.SendMain("New license granted: {0}{1}{2} -{3} {4} {5}({6}, {7})",
                                 Colors.OLIVE, SteamProxy.GetPackageName(license.PackageID), Colors.NORMAL,
                                 Colors.DARK_BLUE, SteamDB.GetPackageURL(license.PackageID), Colors.NORMAL,
                                 license.LicenseType, license.PaymentMethod
                                 );
                }

                OwnedPackages.Add(license.PackageID);
            }

            // TODO: Probably should handle deletions too, for OwnedPackages
        }
Пример #2
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)
                                                  );
                    }
                });
            }
        }
Пример #3
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")
                    );
            }
        }
Пример #4
0
        public override async Task OnCommand(CommandArguments command)
        {
            uint subID;

            if (command.Message.Length == 0 || !uint.TryParse(command.Message, out subID))
            {
                command.Reply("Usage:{0} sub <subid>", Colors.OLIVE);

                return;
            }

            var job = await Steam.Instance.Apps.PICSGetProductInfo(null, subID, false, false);

            var callback = job.Results.FirstOrDefault(x => x.Packages.ContainsKey(subID));

            if (callback == null)
            {
                command.Reply("Unknown SubID: {0}{1}{2}", Colors.BLUE, subID, LicenseList.OwnedSubs.ContainsKey(subID) ? SteamDB.StringCheckmark : string.Empty);

                return;
            }

            var info = callback.Packages[subID];

            info.KeyValues.SaveToFile(Path.Combine(Application.Path, "sub", string.Format("{0}.vdf", info.ID)), false);

            command.Reply("{0}{1}{2} -{3} {4}{5} - Dump:{6} {7}{8}{9}{10}",
                          Colors.BLUE, Steam.GetPackageName(info.ID), 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
                          );
        }
Пример #5
0
        public override async Task OnCommand(CommandArguments command)
        {
            BlogPost post;

            await using (var db = await Database.GetConnectionAsync())
            {
                post = (await db.QueryAsync <BlogPost>("SELECT `ID`, `Slug`, `Title` FROM `Blog` WHERE `IsHidden` = 0 ORDER BY `Time` DESC LIMIT 1")).SingleOrDefault();
            }

            if (post.ID == 0)
            {
                command.Reply("No blog post found.");

                return;
            }

            command.Reply(
                command.Message.Length > 0 ?
                "Blog post:{0} {1}{2} -{3} {4}" :
                "Latest blog post:{0} {1}{2} -{3} {4}",

                Colors.BLUE, post.Title, Colors.NORMAL,
                Colors.DARKBLUE, SteamDB.GetBlogUrl(post.Slug.Length > 0 ? post.Slug : post.ID.ToString())
                );
        }
        private void PrintImportants(SteamApps.PICSChangesCallback callback)
        {
            // Apps
            var    important = callback.AppChanges.Keys.Intersect(Application.ImportantApps.Keys);
            string appType;
            string appName;

            foreach (var app in important)
            {
                appName = Steam.GetAppName(app, out appType);

                IRC.Instance.AnnounceImportantAppUpdate(app, "{0} update: {1}{2}{3} -{4} {5}",
                                                        appType,
                                                        Colors.BLUE, appName, Colors.NORMAL,
                                                        Colors.DARKBLUE, SteamDB.GetAppURL(app, "history"));
            }

            // Packages
            important = callback.PackageChanges.Keys.Intersect(Application.ImportantSubs.Keys);

            foreach (var package in important)
            {
                IRC.Instance.AnnounceImportantPackageUpdate(package, "Package update: {0}{1}{2} -{3} {4}", Colors.BLUE, Steam.GetPackageName(package), Colors.NORMAL, Colors.DARKBLUE, SteamDB.GetPackageURL(package, "history"));
            }
        }
Пример #7
0
        private void PrintImportants(SteamApps.PICSChangesCallback callback)
        {
            // Apps
            var important = callback.AppChanges.Keys.Intersect(Application.ImportantApps.Keys);

            foreach (var app in important)
            {
                var appName = Steam.GetAppName(app, out var appType);

                IRC.Instance.AnnounceImportantAppUpdate(app, "{0} update: {1}{2}{3} -{4} {5}",
                                                        appType,
                                                        Colors.BLUE, appName, Colors.NORMAL,
                                                        Colors.DARKBLUE, SteamDB.GetAppURL(app, "history"));

                if (Settings.Current.CanQueryStore)
                {
                    Steam.Instance.UnifiedMessages.SendMessage("ChatRoom.SendChatMessage#1", new CChatRoom_SendChatMessage_Request
                    {
                        chat_group_id = 1147,
                        chat_id       = 10208600,
                        message       = $"[Changelist {callback.CurrentChangeNumber}] {appType} update: {appName}\n{SteamDB.GetAppURL(app, "history")}?utm_source=Steam&utm_medium=Steam&utm_campaign=SteamDB%20Group%20Chat"
                    });
                }
            }

            // Packages
            important = callback.PackageChanges.Keys.Intersect(Application.ImportantSubs.Keys);

            foreach (var package in important)
            {
                IRC.Instance.AnnounceImportantPackageUpdate(package, "Package update: {0}{1}{2} -{3} {4}", Colors.BLUE, Steam.GetPackageName(package), Colors.NORMAL, Colors.DARKBLUE, SteamDB.GetPackageURL(package, "history"));
            }
        }
Пример #8
0
        private static void PrintImportants(SteamApps.PICSChangesCallback callback)
        {
            // Apps
            var important = callback.AppChanges.Keys.Intersect(Application.ImportantApps.Keys);

            foreach (var app in important)
            {
                var appName = Steam.GetAppName(app, out var appType);

                IRC.Instance.AnnounceImportantAppUpdate(app, $"{appType} update: {Colors.BLUE}{appName}{Colors.NORMAL} -{Colors.DARKBLUE} {SteamDB.GetAppUrl(app, "history")}");

                if (Settings.Current.CanQueryStore)
                {
                    Steam.Instance.UnifiedMessages.SendMessage("ChatRoom.SendChatMessage#1", new CChatRoom_SendChatMessage_Request
                    {
                        chat_group_id = 1147,
                        chat_id       = 10208600,
                        message       = $"{appType} update: {appName}\n<{SteamDB.GetAppUrl(app, "history")}?changeid={callback.CurrentChangeNumber}>"
                    });
                }
            }

            // Packages
            important = callback.PackageChanges.Keys.Intersect(Application.ImportantSubs.Keys);

            foreach (var package in important)
            {
                IRC.Instance.SendMain($"Package update: {Colors.BLUE}{Steam.GetPackageName(package)}{Colors.NORMAL} -{Colors.DARKBLUE} {SteamDB.GetPackageUrl(package, "history")}");
            }
        }
Пример #9
0
        public override void OnCommand(CommandArguments command)
        {
            BlogPost post;

            using (var db = Database.GetConnection())
            {
                if (command.Message.Length > 0)
                {
                    post = db.Query <BlogPost>("SELECT `ID`, `Slug`, `Title` FROM `Blog` WHERE `IsHidden` = 0 AND (`Slug` = @Slug OR `ID` = @Slug) LIMIT 1", new { Slug = command.Message }).SingleOrDefault();
                }
                else
                {
                    post = db.Query <BlogPost>("SELECT `ID`, `Slug`, `Title` FROM `Blog` WHERE `IsHidden` = 0 ORDER BY `Time` DESC LIMIT 1").SingleOrDefault();
                }
            }

            if (post.ID == 0)
            {
                CommandHandler.ReplyToCommand(command, "No blog post found.");

                return;
            }

            CommandHandler.ReplyToCommand(
                command,

                command.Message.Length > 0 ?
                "Blog post:{0} {1}{2} -{3} {4}" :
                "Latest blog post:{0} {1}{2} -{3} {4}",

                Colors.BLUE, post.Title, Colors.NORMAL,
                Colors.DARKBLUE, SteamDB.GetBlogURL(post.Slug.Length > 0 ? post.Slug : post.ID.ToString())
                );
        }
Пример #10
0
        public override async Task OnCommand(CommandArguments command)
        {
            if (command.Message.Length == 0 || !uint.TryParse(command.Message, out var subID))
            {
                command.Reply("Usage:{0} sub <subid>", Colors.OLIVE);

                return;
            }

            var tokenTask = Steam.Instance.Apps.PICSGetAccessTokens(null, subID);

            tokenTask.Timeout = TimeSpan.FromSeconds(10);
            var tokenCallback = await tokenTask;

            SteamApps.PICSRequest request;

            if (tokenCallback.PackageTokens.ContainsKey(subID))
            {
                request = PICSTokens.NewPackageRequest(subID, tokenCallback.PackageTokens[subID]);
            }
            else
            {
                request = PICSTokens.NewPackageRequest(subID);
            }

            var infoTask = Steam.Instance.Apps.PICSGetProductInfo(Enumerable.Empty <SteamApps.PICSRequest>(), new List <SteamApps.PICSRequest> {
                request
            });

            infoTask.Timeout = TimeSpan.FromSeconds(10);
            var job      = await infoTask;
            var callback = job.Results.FirstOrDefault(x => x.Packages.ContainsKey(subID));

            if (callback == null)
            {
                command.Reply("Unknown SubID: {0}{1}{2}", Colors.BLUE, subID, LicenseList.OwnedSubs.ContainsKey(subID) ? SteamDB.StringCheckmark : string.Empty);

                return;
            }

            var info = callback.Packages[subID];

            info.KeyValues.SaveToFile(Path.Combine(Application.Path, "sub", string.Format("{0}.vdf", info.ID)), false);

            command.Reply("{0}{1}{2} -{3} {4}{5} - Dump:{6} {7}{8}{9}{10}",
                          Colors.BLUE, Steam.GetPackageName(info.ID), 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
                          );
        }
Пример #11
0
        public override async Task OnCommand(CommandArguments command)
        {
            if (string.IsNullOrWhiteSpace(command.Message))
            {
                command.Reply($"Usage:{Colors.OLIVE} app <appid or partial game name>");

                return;
            }

            if (!uint.TryParse(command.Message, out var appID))
            {
                appID = await TrySearchAppId(command);

                if (appID == 0)
                {
                    return;
                }
            }

            var info = await GetAppData(appID);

            if (info == null)
            {
                command.Reply($"Unknown AppID: {Colors.BLUE}{appID}{(LicenseList.OwnedApps.ContainsKey(appID) ? SteamDB.StringCheckmark : string.Empty)}");

                return;
            }

            string name;

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

            var filename = $"{Utils.ByteArrayToString(info.SHAHash)}.vdf";

            info.KeyValues.SaveToFile(Path.Combine(Application.Path, "app", filename), false);

            command.Reply($"{Colors.BLUE}{name}{Colors.NORMAL} -{Colors.DARKBLUE} <{SteamDB.GetAppUrl(info.ID)}>{Colors.NORMAL} - Dump:{Colors.DARKBLUE} <{SteamDB.GetRawAppUrl(filename)}>{Colors.NORMAL}{(info.MissingToken ? SteamDB.StringNeedToken : string.Empty)}{(LicenseList.OwnedApps.ContainsKey(info.ID) ? SteamDB.StringCheckmark : string.Empty)}");

            if (command.Recipient == Settings.Current.IRC.Channel.Ops && !LicenseList.OwnedApps.ContainsKey(info.ID))
            {
                JobManager.AddJob(() => Steam.Instance.Apps.RequestFreeLicense(info.ID));
            }
        }
Пример #12
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)
                                                  );
                    }
                });
            }
        }
Пример #13
0
        public override async void OnCommand(CommandArguments command)
        {
            uint subID;

            if (command.Message.Length == 0 || !uint.TryParse(command.Message, out subID))
            {
                CommandHandler.ReplyToCommand(command, "Usage:{0} sub <subid>", Colors.OLIVE);

                return;
            }

            var count = PICSProductInfo.ProcessedSubs.Count;

            if (count > 100)
            {
                CommandHandler.ReplyToCommand(command, "There are currently {0} packages awaiting to be processed, try again later.", count);

                return;
            }

            var job = await Steam.Instance.Apps.PICSGetProductInfo(null, subID, false, false);

            var callback = job.Results.First(x => !x.ResponsePending);

            if (!callback.Packages.ContainsKey(subID))
            {
                CommandHandler.ReplyToCommand(command, "Unknown SubID: {0}{1}{2}", Colors.BLUE, subID, LicenseList.OwnedSubs.ContainsKey(subID) ? SteamDB.StringCheckmark : string.Empty);

                return;
            }

            var info = callback.Packages[subID];

            info.KeyValues.SaveToFile(Path.Combine(Application.Path, "sub", string.Format("{0}.vdf", info.ID)), false);

            CommandHandler.ReplyToCommand(command, "{0}{1}{2} -{3} {4}{5} - Dump:{6} {7}{8}{9}{10}",
                                          Colors.BLUE, Steam.GetPackageName(info.ID), 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
                                          );
        }
Пример #14
0
        private static void PrintImportants(SteamApps.PICSChangesCallback callback)
        {
            // Apps
            var important = callback.AppChanges.Keys.Intersect(Application.ImportantApps);

            foreach (var app in important)
            {
                var changeNumber = callback.AppChanges[app].ChangeNumber;

                TaskManager.Run(async() => await Utils.SendWebhook(new
                {
                    Type         = "ImportantAppUpdate",
                    AppID        = app,
                    ChangeNumber = changeNumber,
                    Url          = $"{SteamDB.GetAppUrl(app, "history")}?changeid={changeNumber}",
                }));

                var appName = Steam.GetAppName(app, out var appType);
                IRC.Instance.SendAnnounce($"{appType} update: {Colors.BLUE}{appName}{Colors.NORMAL} -{Colors.DARKBLUE} {SteamDB.GetAppUrl(app, "history")}");
            }

            // Packages
            important = callback.PackageChanges.Keys.Intersect(Application.ImportantSubs);

            foreach (var package in important)
            {
                var changeNumber = callback.PackageChanges[package].ChangeNumber;

                TaskManager.Run(async() => await Utils.SendWebhook(new
                {
                    Type         = "ImportantSubUpdate",
                    SubID        = package,
                    ChangeNumber = changeNumber,
                    Url          = $"{SteamDB.GetPackageUrl(package, "history")}?changeid={changeNumber}",
                }));

                var subName = Steam.GetPackageName(package);
                IRC.Instance.SendAnnounce($"Package update: {Colors.BLUE}{subName}{Colors.NORMAL} -{Colors.DARKBLUE} {SteamDB.GetPackageUrl(package, "history")}");
            }
        }
Пример #15
0
        public void OnNumberOfPlayers(SteamUserStats.NumberOfPlayersCallback callback, JobID jobID)
        {
            var request = IRCRequests.Find(r => r.JobID == jobID);

            if (request == null)
            {
                return;
            }

            IRCRequests.Remove(request);

            if (callback.Result != EResult.OK)
            {
                CommandHandler.ReplyToCommand(request.Command, "{0}{1}{2}: Unable to request player count: {3}", Colors.OLIVE, request.Command.Nickname, Colors.NORMAL, callback.Result);
            }
            else if (request.Target == 0)
            {
                CommandHandler.ReplyToCommand(request.Command, "{0}{1}{2}: {3}{4:N0}{5} people praising lord Gaben right now, influence:{6} {7}", Colors.OLIVE, request.Command.Nickname, Colors.NORMAL, Colors.OLIVE, callback.NumPlayers, Colors.NORMAL, Colors.DARK_BLUE, SteamDB.GetGraphURL(0));
            }
            else
            {
                string graphUrl = string.Empty;
                string name     = GetAppName(request.Target);

                using (MySqlDataReader Reader = DbWorker.ExecuteReader("SELECT `AppID` FROM `ImportantApps` WHERE (`Graph` = 1 OR `MaxPlayers` > 0) AND `AppID` = @AppID", new MySqlParameter("AppID", request.Target)))
                {
                    if (Reader.Read())
                    {
                        graphUrl = string.Format("{0} -{1} {2}", Colors.NORMAL, Colors.DARK_BLUE, SteamDB.GetGraphURL(request.Target));
                    }
                }

                CommandHandler.ReplyToCommand(request.Command, "{0}{1}{2}: People playing {3}{4}{5} right now: {6}{7:N0}{8} -{9} {10}{11}",
                                              Colors.OLIVE, request.Command.Nickname, Colors.NORMAL,
                                              Colors.OLIVE, name, Colors.NORMAL,
                                              Colors.GREEN, callback.NumPlayers, Colors.NORMAL,
                                              Colors.DARK_BLUE, SteamDB.GetAppURL(request.Target), graphUrl);
            }
        }
Пример #16
0
        public void OnChatMessage(SteamFriends.ChatMsgCallback callback)
        {
            if (callback.ChatMsgType != EChatEntryType.ChatMsg || callback.Message[0] != '!' || callback.Message.Contains('\n'))
            {
                return;
            }

            var i            = callback.Message.IndexOf(' ');
            var inputCommand = i == -1 ? callback.Message : callback.Message.Substring(0, i);

            Action <CommandHandler.CommandArguments> callbackFunction;

            if (CommandHandler.Commands.TryGetValue(inputCommand, out callbackFunction))
            {
                var input = i == -1 ? string.Empty : callback.Message.Substring(i).Trim();

                var command = new CommandHandler.CommandArguments
                {
                    SenderID   = callback.ChatterID,
                    ChatRoomID = callback.ChatRoomID,
                    Nickname   = Steam.Instance.Friends.GetFriendPersonaName(callback.ChatterID),
                    Message    = input
                };

                if (SteamDB.IsBusy())
                {
                    CommandHandler.ReplyToCommand(command, "{0}{1}{2}: The bot is currently busy.", Colors.OLIVE, command.Nickname, Colors.NORMAL);

                    return;
                }

                Log.WriteInfo("Steam", "Handling command {0} for user {1} in chatroom {2}", inputCommand, callback.ChatterID, callback.ChatRoomID);

                callbackFunction(command);
            }
        }
Пример #17
0
        public override async Task OnCommand(CommandArguments command)
        {
            if (command.Message.Length == 0)
            {
                command.Reply("Usage:{0} app <appid or partial game name>", Colors.OLIVE);

                return;
            }

            string name;

            if (!uint.TryParse(command.Message, out var appID))
            {
                name = command.Message;

                if (!Utils.ConvertUserInputToSQLSearch(ref name))
                {
                    command.Reply("Your request is invalid or too short.");

                    return;
                }

                using (var db = Database.GetConnection())
                {
                    appID = db.ExecuteScalar <uint>("SELECT `AppID` FROM `Apps` WHERE `Apps`.`StoreName` LIKE @Name OR `Apps`.`Name` LIKE @Name OR `Apps`.`LastKnownName` LIKE @Name ORDER BY `LastUpdated` DESC LIMIT 1", new { Name = name });
                }

                if (appID == 0)
                {
                    command.Reply("Nothing was found matching your request.");

                    return;
                }
            }

            var tokenCallback = await Steam.Instance.Apps.PICSGetAccessTokens(appID, null);

            SteamApps.PICSRequest request;

            if (tokenCallback.AppTokens.ContainsKey(appID))
            {
                request = Utils.NewPICSRequest(appID, tokenCallback.AppTokens[appID]);
            }
            else
            {
                request = Utils.NewPICSRequest(appID);
            }

            var job = await Steam.Instance.Apps.PICSGetProductInfo(new List <SteamApps.PICSRequest> {
                request
            }, Enumerable.Empty <SteamApps.PICSRequest>());

            var callback = job.Results.FirstOrDefault(x => x.Apps.ContainsKey(appID));

            if (callback == null)
            {
                command.Reply("Unknown AppID: {0}{1}{2}", Colors.BLUE, appID, LicenseList.OwnedApps.ContainsKey(appID) ? SteamDB.StringCheckmark : string.Empty);

                return;
            }

            var info = callback.Apps[appID];

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

            info.KeyValues.SaveToFile(Path.Combine(Application.Path, "app", string.Format("{0}.vdf", info.ID)), false);

            command.Reply("{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
                          );

            if (command.IsUserAdmin && !LicenseList.OwnedApps.ContainsKey(info.ID))
            {
                JobManager.AddJob(() => Steam.Instance.Apps.RequestFreeLicense(info.ID));
            }
        }
Пример #18
0
        public override async Task OnCommand(CommandArguments command)
        {
            if (command.Message.Length == 0)
            {
                command.Reply("Usage:{0} pubfile <pubfileid>", Colors.OLIVE);

                return;
            }

            if (!ulong.TryParse(command.Message, out var pubFileId))
            {
                command.Reply("Invalid Published File ID");

                return;
            }

            var pubFileRequest = new CPublishedFile_GetDetails_Request
            {
                includeadditionalpreviews = true,
                includechildren           = true,
                includetags        = true,
                includekvtags      = true,
                includevotes       = true,
                includeforsaledata = true,
                includemetadata    = true,
            };

            pubFileRequest.publishedfileids.Add(pubFileId);

            var task = PublishedFiles.SendMessage(api => api.GetDetails(pubFileRequest));

            task.Timeout = TimeSpan.FromSeconds(10);
            var callback = await task;
            var response = callback.GetDeserializedResponse <CPublishedFile_GetDetails_Response>();
            var details  = response.publishedfiledetails.FirstOrDefault();

            if (details == null)
            {
                command.Reply("Unable to make service request for published file info: the server returned no info");

                return;
            }

            var result = (EResult)details.result;

            if (result != EResult.OK)
            {
                command.Reply("Unable to get published file info: {0}{1}", Colors.RED, result);

                return;
            }

            var json = JsonConvert.SerializeObject(details, Formatting.Indented);

            File.WriteAllText(Path.Combine(Application.Path, "ugc", string.Format("{0}.json", details.publishedfileid)), json, Encoding.UTF8);

            command.Reply("{0}, Title: {1}{2}{3}, Creator: {4}{5}{6}, App: {7}{8}{9}{10}, File UGC: {11}{12}{13}, Preview UGC: {14}{15}{16} -{17} {18}",
                          (EWorkshopFileType)details.file_type,
                          Colors.BLUE, string.IsNullOrWhiteSpace(details.title) ? "[no title]" : details.title, Colors.NORMAL,
                          Colors.BLUE, new SteamID(details.creator).Render(true), Colors.NORMAL,
                          Colors.BLUE, details.creator_appid,
                          details.creator_appid == details.consumer_appid ? "" : string.Format(" (consumer {0})", details.consumer_appid),
                          Colors.NORMAL,
                          Colors.BLUE, details.hcontent_file, Colors.NORMAL,
                          Colors.BLUE, details.hcontent_preview, Colors.NORMAL,
                          Colors.DARKBLUE, SteamDB.GetUGCURL(details.publishedfileid)
                          );

            command.Notice("{0} - https://steamcommunity.com/sharedfiles/filedetails/?id={1}", details.file_url, details.publishedfileid);
        }
Пример #19
0
        private async Task SendChangelistsToIRC(SteamApps.PICSChangesCallback callback)
        {
            if (DateTime.Now > ChangelistBurstTime)
            {
                ChangelistBurstTime  = DateTime.Now.AddMinutes(5);
                ChangelistBurstCount = 0;
            }

            // Group apps and package changes by changelist number
            var changelists = new Dictionary <uint, IrcChangelistGroup>();

            foreach (var app in callback.AppChanges.Values)
            {
                if (!changelists.ContainsKey(app.ChangeNumber))
                {
                    changelists[app.ChangeNumber] = new IrcChangelistGroup();
                }

                changelists[app.ChangeNumber].Apps.Add(app.ID);
            }

            foreach (var package in callback.PackageChanges.Values)
            {
                if (!changelists.ContainsKey(package.ChangeNumber))
                {
                    changelists[package.ChangeNumber] = new IrcChangelistGroup();
                }

                changelists[package.ChangeNumber].Packages.Add(package.ID);
            }

            foreach (var(changeNumber, changeList) in changelists.OrderBy(x => x.Key))
            {
                var appCount     = changeList.Apps.Count;
                var packageCount = changeList.Packages.Count;

                var message = $"Changelist {Colors.BLUE}{changeNumber}{Colors.NORMAL} {Colors.DARKGRAY}({appCount:N0} apps and {packageCount:N0} packages)";

                var changesCount = appCount + packageCount;

                if (changesCount >= 50)
                {
                    IRC.Instance.SendMain($"Big {message}{Colors.DARKBLUE} {SteamDB.GetChangelistUrl(changeNumber)}");
                }

                if (ChangelistBurstCount++ >= CHANGELIST_BURST_MIN || changesCount > 300)
                {
                    if (appCount > 0)
                    {
                        message += $" (Apps: {string.Join(", ", changeList.Apps)})";
                    }

                    if (packageCount > 0)
                    {
                        message += $" (Packages: {string.Join(", ", changeList.Packages)})";
                    }

                    IRC.Instance.SendAnnounce($"{Colors.RED}»{Colors.NORMAL} {message}");

                    continue;
                }

                IRC.Instance.SendAnnounce($"{Colors.RED}»{Colors.NORMAL} {message}");

                if (appCount > 0)
                {
                    Dictionary <uint, App> apps;

                    await using (var db = await Database.GetConnectionAsync())
                    {
                        apps = (await db.QueryAsync <App>("SELECT `AppID`, `Name`, `LastKnownName` FROM `Apps` WHERE `AppID` IN @Ids", new { Ids = changeList.Apps })).ToDictionary(x => x.AppID, x => x);
                    }

                    foreach (var appId in changeList.Apps)
                    {
                        apps.TryGetValue(appId, out var data);

                        IRC.Instance.SendAnnounce($"  App: {Colors.BLUE}{appId}{Colors.NORMAL} - {Steam.FormatAppName(appId, data)}");
                    }
                }

                if (packageCount > 0)
                {
                    Dictionary <uint, Package> packages;

                    await using (var db = await Database.GetConnectionAsync())
                    {
                        packages = (await db.QueryAsync <Package>("SELECT `SubID`, `Name`, `LastKnownName` FROM `Subs` WHERE `SubID` IN @Ids", new { Ids = changeList.Packages })).ToDictionary(x => x.SubID, x => x);
                    }

                    foreach (var packageId in changeList.Packages)
                    {
                        packages.TryGetValue(packageId, out var data);

                        IRC.Instance.SendAnnounce($"  Package: {Colors.BLUE}{packageId}{Colors.NORMAL} - {Steam.FormatPackageName(packageId, data)}");
                    }
                }
            }
        }
        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);
        }
Пример #21
0
        private void SendChangelistsToIRC(SteamApps.PICSChangesCallback callback)
        {
            // Group apps and package changes by changelist, this will seperate into individual changelists
            var appGrouping     = callback.AppChanges.Values.GroupBy(a => a.ChangeNumber);
            var packageGrouping = callback.PackageChanges.Values.GroupBy(p => p.ChangeNumber);

            // Join apps and packages back together based on changelist number
            var changeLists = Utils.FullOuterJoin(appGrouping, packageGrouping, a => a.Key, p => p.Key, (a, p, key) => new
            {
                ChangeNumber = key,

                Apps     = a.ToList(),
                Packages = p.ToList(),
            },
                                                  new EmptyGrouping <uint, SteamApps.PICSChangesCallback.PICSChangeData>(),
                                                  new EmptyGrouping <uint, SteamApps.PICSChangesCallback.PICSChangeData>())
                              .OrderBy(c => c.ChangeNumber);

            foreach (var changeList in changeLists)
            {
                var appCount     = changeList.Apps.Count;
                var packageCount = changeList.Packages.Count;

                string Message = string.Format("Changelist {0}{1}{2} {3}({4:N0} apps and {5:N0} packages){6} -{7} {8}",
                                               Colors.BLUE, changeList.ChangeNumber, Colors.NORMAL,
                                               Colors.DARKGRAY, appCount, packageCount, Colors.NORMAL,
                                               Colors.DARKBLUE, SteamDB.GetChangelistURL(changeList.ChangeNumber)
                                               );

                var changesCount = appCount + packageCount;

                if (changesCount >= 50)
                {
                    IRC.Instance.SendMain(Message);
                }

                IRC.Instance.SendAnnounce("{0}»{1} {2}", Colors.RED, Colors.NORMAL, Message);

                // If this changelist is very big, freenode will hate us forever if we decide to print all that stuff
                if (changesCount > 300)
                {
                    IRC.Instance.SendAnnounce("{0}  This changelist is too big to be printed in IRC, please view it online", Colors.RED);

                    continue;
                }

                if (appCount > 0)
                {
                    Dictionary <uint, App> apps;
                    App data;

                    using (var db = Database.GetConnection())
                    {
                        apps = db.Query <App>("SELECT `AppID`, `Name`, `LastKnownName` FROM `Apps` WHERE `AppID` IN @Ids", new { Ids = changeList.Apps.Select(x => x.ID) }).ToDictionary(x => x.AppID, x => x);
                    }

                    foreach (var app in changeList.Apps)
                    {
                        apps.TryGetValue(app.ID, out data);

                        IRC.Instance.SendAnnounce("  App: {0}{1}{2} - {3}{4}",
                                                  Colors.BLUE, app.ID, Colors.NORMAL,
                                                  Steam.FormatAppName(app.ID, data),
                                                  app.NeedsToken ? SteamDB.StringNeedToken : string.Empty
                                                  );
                    }
                }

                if (packageCount > 0)
                {
                    Dictionary <uint, Package> packages;
                    Package data;

                    using (var db = Database.GetConnection())
                    {
                        packages = db.Query <Package>("SELECT `SubID`, `Name`, `LastKnownName` FROM `Subs` WHERE `SubID` IN @Ids", new { Ids = changeList.Packages.Select(x => x.ID) }).ToDictionary(x => x.SubID, x => x);
                    }

                    foreach (var package in changeList.Packages)
                    {
                        packages.TryGetValue(package.ID, out data);

                        IRC.Instance.SendAnnounce("  Package: {0}{1}{2} - {3}{4}",
                                                  Colors.BLUE, package.ID, Colors.NORMAL,
                                                  Steam.FormatPackageName(package.ID, data),
                                                  package.NeedsToken ? SteamDB.StringNeedToken : string.Empty
                                                  );
                    }
                }
            }
        }
Пример #22
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)}");
                    }
                });
            }
        }
Пример #23
0
        public void Process(SteamApps.PICSProductInfoCallback.PICSProductInfo productInfo)
        {
            ChangeNumber = productInfo.ChangeNumber;

            if (Settings.IsFullRun)
            {
                Log.WriteDebug("App Processor", "AppID: {0}", AppID);

                DbConnection.Execute("INSERT INTO `Changelists` (`ChangeID`) VALUES (@ChangeNumber) ON DUPLICATE KEY UPDATE `Date` = `Date`", new { productInfo.ChangeNumber });
                DbConnection.Execute("INSERT INTO `ChangelistsApps` (`ChangeID`, `AppID`) VALUES (@ChangeNumber, @AppID) ON DUPLICATE KEY UPDATE `AppID` = `AppID`", new { AppID, productInfo.ChangeNumber });
            }

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

            var newAppName = productInfo.KeyValues["common"]["name"].AsString();

            if (newAppName != null)
            {
                int    newAppType  = -1;
                string currentType = productInfo.KeyValues["common"]["type"].AsString().ToLower();

                using (var reader = DbConnection.ExecuteReader("SELECT `AppType` FROM `AppsTypes` WHERE `Name` = @Type LIMIT 1", new { Type = currentType }))
                {
                    if (reader.Read())
                    {
                        newAppType = reader.GetInt32(reader.GetOrdinal("AppType"));
                    }
                }

                if (newAppType == -1)
                {
                    DbConnection.Execute("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("App Processor", "Creating new apptype \"{0}\" (AppID {1})", currentType, AppID);

                    IRC.Instance.SendOps("New app type: {0}{1}{2} - {3}", Colors.BLUE, currentType, Colors.NORMAL, SteamDB.GetAppURL(AppID, "history"));

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

                if (string.IsNullOrEmpty(app.Name) || app.Name.StartsWith(SteamDB.UNKNOWN_APP, StringComparison.Ordinal))
                {
                    DbConnection.Execute("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 }
                                         );

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

                    // TODO: Testy testy
                    if (!Settings.IsFullRun && Settings.Current.ChatRooms.Count > 0)
                    {
                        Steam.Instance.Friends.SendChatRoomMessage(Settings.Current.ChatRooms[0], EChatEntryType.ChatMsg,
                                                                   string.Format(
                                                                       "New {0} was published: {1}\nSteamDB: {2}\nSteam: https://store.steampowered.com/app/{3}/",
                                                                       currentType,
                                                                       newAppName,
                                                                       SteamDB.GetAppURL(AppID),
                                                                       AppID
                                                                       )
                                                                   );
                    }

                    if ((newAppType > 9 && newAppType != 13) || Triggers.Any(newAppName.Contains))
                    {
                        IRC.Instance.SendOps("New {0}: {1}{2}{3} -{4} {5}",
                                             currentType,
                                             Colors.BLUE, newAppName, Colors.NORMAL,
                                             Colors.DARKBLUE, SteamDB.GetAppURL(AppID, "history"));
                    }
                }
                else if (!app.Name.Equals(newAppName))
                {
                    DbConnection.Execute("UPDATE `Apps` SET `Name` = @AppName, `LastKnownName` = @AppName WHERE `AppID` = @AppID", new { AppID, AppName = newAppName });

                    MakeHistory("modified_info", SteamDB.DATABASE_NAME_TYPE, app.Name, newAppName);
                }

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

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

            foreach (var section in productInfo.KeyValues.Children)
            {
                string sectionName = section.Name.ToLower();

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

                if (sectionName == "change_number") // Carefully handle change_number
                {
                    sectionName = "root_change_number";

                    // TODO: Remove this key, move it to Apps table itself
                    ProcessKey(sectionName, "change_number", productInfo.ChangeNumber.ToString()); //section.AsString());
                }
                else if (sectionName == "common" || sectionName == "extended")
                {
                    string keyName;

                    foreach (KeyValue keyvalue in section.Children)
                    {
                        keyName = string.Format("{0}_{1}", sectionName, keyvalue.Name);

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

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

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

            foreach (var data in CurrentData.Values)
            {
                if (!data.Processed && !data.KeyName.StartsWith("website", StringComparison.Ordinal))
                {
                    DbConnection.Execute("DELETE FROM `AppsInfo` WHERE `AppID` = @AppID AND `Key` = @Key", new { AppID, data.Key });

                    MakeHistory("removed_key", data.Key, data.Value);

                    if (newAppName != null && data.Key.Equals("common_section_type") && data.Value.Equals("ownersonly"))
                    {
                        IRC.Instance.SendMain("Removed ownersonly from: {0}{1}{2} -{3} {4}", Colors.BLUE, app.Name, Colors.NORMAL, Colors.DARKBLUE, SteamDB.GetAppURL(AppID, "history"));
                    }
                }
            }

            if (newAppName == null)
            {
                if (string.IsNullOrEmpty(app.Name)) // We don't have the app in our database yet
                {
                    DbConnection.Execute("INSERT INTO `Apps` (`AppID`, `Name`) VALUES (@AppID, @AppName) ON DUPLICATE KEY UPDATE `AppType` = `AppType`", new { AppID, AppName = string.Format("{0} {1}", SteamDB.UNKNOWN_APP, AppID) });
                }
                else if (!app.Name.StartsWith(SteamDB.UNKNOWN_APP, StringComparison.Ordinal)) // We do have the app, replace it with default name
                {
                    IRC.Instance.SendMain("App deleted: {0}{1}{2} -{3} {4}", Colors.BLUE, app.Name, Colors.NORMAL, Colors.DARKBLUE, SteamDB.GetAppURL(AppID, "history"));

                    DbConnection.Execute("UPDATE `Apps` SET `Name` = @AppName, `AppType` = 0 WHERE `AppID` = @AppID", new { AppID, AppName = string.Format("{0} {1}", SteamDB.UNKNOWN_APP, AppID) });

                    MakeHistory("deleted_app", 0, app.Name);
                }
            }

            if (productInfo.KeyValues["depots"] != null)
            {
                Steam.Instance.DepotProcessor.Process(AppID, ChangeNumber, productInfo.KeyValues["depots"]);
            }
        }
Пример #24
0
        private static async Task ProcessFeed(Uri feedUrl)
        {
            var feed = await LoadRSS(feedUrl);

            if (feed == null)
            {
                return;
            }

            if (feed.Items.Count == 0)
            {
                Log.WriteError(nameof(RSS), $"Did not find any items in {feedUrl}");
                return;
            }

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

            var items = (await db.QueryAsync <GenericFeedItem>("SELECT `Link` FROM `RSS` WHERE `Link` IN @Ids", new { Ids = feed.Items.Select(x => x.Link) })).ToDictionary(x => x.Link, _ => (byte)1);

            var newItems = feed.Items.Where(item => !items.ContainsKey(item.Link));

            foreach (var item in newItems)
            {
                Log.WriteInfo(nameof(RSS), $"[{feed.Title}] {item.Title}: {item.Link}");

                IRC.Instance.SendAnnounce($"{Colors.BLUE}{feed.Title}{Colors.NORMAL}: {item.Title} -{Colors.DARKBLUE} {item.Link}");

                await db.ExecuteAsync("INSERT INTO `RSS` (`Link`, `Title`) VALUES(@Link, @Title)", new { item.Link, item.Title });

                _ = TaskManager.Run(async() => await Utils.SendWebhook(new
                {
                    Type = "RSS",
                    item.Title,
                    Url = item.Link,
                }));

                if (!Settings.IsMillhaven)
                {
                    continue;
                }

                uint appID = 0;

                if (feed.Title == "Steam RSS News Feed")
                {
                    if (item.Title.StartsWith("Dota 2 Update", StringComparison.Ordinal))
                    {
                        appID = 570;
                    }
                    else if (item.Title == "Team Fortress 2 Update Released")
                    {
                        appID = 440;

                        // tf2 changelog cleanup
                        item.Content = item.Content.Replace("<br/>", "\n");
                        item.Content = item.Content.Replace("<ul style=\"padding-bottom: 0px; margin-bottom: 0px;\">", "\n");
                        item.Content = item.Content.Replace("<ul style=\"padding-bottom: 0px; margin-bottom: 0px;\" >", "\n");
                        item.Content = item.Content.Replace("</ul>", "\n");
                        item.Content = item.Content.Replace("<li>", "* ");
                    }
                    else if (item.Title == "Left 4 Dead 2 - Update")
                    {
                        appID = 550;
                    }
                    else if (item.Title == "Left 4 Dead - Update")
                    {
                        appID = 500;
                    }
                    else if (item.Title == "Portal 2 - Update")
                    {
                        appID = 620;
                    }
                }
                else if (feed.Title.Contains("Counter-Strike: Global Offensive") && item.Title.StartsWith("Release Notes", StringComparison.Ordinal))
                {
                    appID = 730;

                    // csgo changelog cleanup
                    item.Content = item.Content.Replace("</p>", "\n");
                    item.Content = new Regex("<p>\\[\\s*(.+)\\s*\\]", RegexOptions.Multiline | RegexOptions.CultureInvariant).Replace(item.Content, "## $1");
                    item.Content = item.Content.Replace("<p>", "");
                }

                if (appID > 0)
                {
                    var build = (await db.QueryAsync <Build>(
                                     "SELECT `Builds`.`BuildID`, `Builds`.`ChangeID`, `Builds`.`AppID`, `Changelists`.`Date`, LENGTH(`Official`) as `Official` FROM `Builds` " +
                                     "LEFT JOIN `Patchnotes` ON `Patchnotes`.`BuildID` = `Builds`.`BuildID` " +
                                     "JOIN `Apps` ON `Apps`.`AppID` = `Builds`.`AppID` " +
                                     "JOIN `Changelists` ON `Builds`.`ChangeID` = `Changelists`.`ChangeID` " +
                                     "WHERE `Builds`.`AppID` = @AppID ORDER BY `Builds`.`BuildID` DESC LIMIT 1",
                                     new { appID }
                                     )).SingleOrDefault();

                    if (build == null)
                    {
                        continue;
                    }

                    if (DateTime.UtcNow > build.Date.AddMinutes(60))
                    {
                        Log.WriteDebug(nameof(RSS), $"Got {appID} update patch notes, but there is no build within last 10 minutes. {item.Link}");
                        IRC.Instance.SendOps($"{Colors.GREEN}[Patch notes]{Colors.NORMAL} Got {appID} update patch notes, but there is no build within last 10 minutes. {item.Link}");
                        continue;
                    }

                    if (build.Official > 0)
                    {
                        Log.WriteDebug(nameof(RSS), $"Got {appID} update patch notes, but official patch notes is already filled. {item.Link}");
                        IRC.Instance.SendOps($"{Colors.GREEN}[Patch notes]{Colors.NORMAL} Got {appID} update patch notes, but official patch notes is already filled. {item.Link}");
                        continue;
                    }

                    // breaks
                    item.Content = new Regex(@"<br( \/)?>\r?\n?", RegexOptions.Multiline | RegexOptions.CultureInvariant).Replace(item.Content, "\n");

                    // dashes (CS:GO mainly)
                    item.Content = new Regex("^(?:-|&#(?:8208|8209|8210|8211|8212|8213);|–|—) ?", RegexOptions.Multiline | RegexOptions.CultureInvariant).Replace(item.Content, "* ");

                    item.Content = WebUtility.HtmlDecode(item.Content);

                    Log.WriteDebug(nameof(RSS), $"Inserting {build.AppID} patchnotes for build {build.BuildID}:\n{item.Content}");

                    var accountId = Steam.Instance.Client?.SteamID?.AccountID ?? 0;

                    await db.ExecuteAsync(
                        "INSERT INTO `Patchnotes` (`BuildID`, `AppID`, `ChangeID`, `Date`, `Official`, `OfficialURL`) " +
                        "VALUES (@BuildID, @AppID, @ChangeID, @Date, @Content, @Link) ON DUPLICATE KEY UPDATE `Official` = VALUES(`Official`), `OfficialURL` = VALUES(`OfficialURL`), `LastEditor` = @AccountID",
                        new
                    {
                        build.BuildID,
                        build.AppID,
                        build.ChangeID,
                        Date = build.Date.AddSeconds(1).ToString("yyyy-MM-dd HH:mm:ss"),
                        item.Content,
                        item.Link,
                        accountId
                    }
                        );

                    IRC.Instance.SendAnnounce($"\u2699 Official patch notes:{Colors.BLUE} {Steam.GetAppName(build.AppID)}{Colors.NORMAL} -{Colors.DARKBLUE} {SteamDB.GetPatchnotesUrl(build.BuildID)}");
                }
            }
        }
Пример #25
0
        private async Task ProcessKey(string keyName, string displayName, string value, bool isJSON = false)
        {
            if (keyName.Length > 90)
            {
                Log.WriteError("Sub Processor", "Key {0} for SubID {1} is too long, not inserting info.", keyName, SubID);

                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.ToLower().Trim();

            if (!CurrentData.ContainsKey(keyName))
            {
                var key = await GetKeyNameID(keyName);

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

                    await DbConnection.ExecuteAsync("INSERT INTO `KeyNamesSubs` (`Name`, `Type`, `DisplayName`) VALUES(@Name, @Type, @DisplayName)", new { Name = keyName, DisplayName = displayName, Type = type });

                    key = await GetKeyNameID(keyName);

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

                        return;
                    }

                    IRC.Instance.SendOps("New package keyname: {0}{1} {2}(ID: {3}) ({4}) - {5}", Colors.BLUE, keyName, Colors.LIGHTGRAY, 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("Sub Processor", "Duplicate key {0} in SubID {1}", keyName, SubID);

                return;
            }

            data.Processed = true;

            CurrentData[keyName] = data;

            if (data.Value.Equals(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);
        }
Пример #26
0
        private static async Task ProcessFeed(Uri feed)
        {
            var rssItems = LoadRSS(feed, out var feedTitle);

            if (rssItems == null)
            {
                return;
            }

            if (rssItems.Count == 0)
            {
                Log.WriteError("RSS", "Did not find any items in {0}", feed);
                return;
            }

            using (var db = await Database.GetConnectionAsync())
            {
                var items = (await db.QueryAsync <GenericFeedItem>("SELECT `Link` FROM `RSS` WHERE `Link` IN @Ids", new { Ids = rssItems.Select(x => x.Link) })).ToDictionary(x => x.Link, x => (byte)1);

                var newItems = rssItems.Where(item => !items.ContainsKey(item.Link));

                foreach (var item in newItems)
                {
                    Log.WriteInfo("RSS", "[{0}] {1}: {2}", feedTitle, item.Title, item.Link);

                    IRC.Instance.SendMain("{0}{1}{2}: {3} -{4} {5}", Colors.BLUE, feedTitle, Colors.NORMAL, item.Title, Colors.DARKBLUE, item.Link);

                    await db.ExecuteAsync("INSERT INTO `RSS` (`Link`, `Title`) VALUES(@Link, @Title)", new { item.Link, item.Title });

                    if (!Settings.Current.CanQueryStore)
                    {
                        continue;
                    }

                    uint appID = 0;

                    if (feedTitle == "Steam RSS News Feed")
                    {
                        if (item.Title.StartsWith("Dota 2 Update", StringComparison.Ordinal))
                        {
                            appID = 570;
                        }
                        else if (item.Title == "Team Fortress 2 Update Released")
                        {
                            appID = 440;
                        }
                        else if (item.Title == "Left 4 Dead 2 - Update")
                        {
                            appID = 550;
                        }
                        else if (item.Title == "Left 4 Dead - Update")
                        {
                            appID = 500;
                        }
                        else if (item.Title == "Portal 2 - Update")
                        {
                            appID = 620;
                        }
                    }
                    else if (feedTitle.Contains("Counter-Strike: Global Offensive") && item.Title.StartsWith("Release Notes", StringComparison.Ordinal))
                    {
                        appID = 730;

                        // csgo changelog cleanup
                        item.Content = item.Content.Replace("</p>", "\n");
                        item.Content = new Regex("<p>\\[\\s*(.+)\\s*\\]", RegexOptions.Multiline | RegexOptions.CultureInvariant).Replace(item.Content, "## $1");
                        item.Content = item.Content.Replace("<p>", "");
                    }

                    if (appID > 0)
                    {
                        var build = (await db.QueryAsync <Build>(
                                         "SELECT `Builds`.`BuildID`, `Builds`.`ChangeID`, `Builds`.`AppID`, `Changelists`.`Date`, LENGTH(`Official`) as `Official` FROM `Builds` " +
                                         "LEFT JOIN `Patchnotes` ON `Patchnotes`.`BuildID` = `Builds`.`BuildID` " +
                                         "JOIN `Apps` ON `Apps`.`AppID` = `Builds`.`AppID` " +
                                         "JOIN `Changelists` ON `Builds`.`ChangeID` = `Changelists`.`ChangeID` " +
                                         "WHERE `Builds`.`AppID` = @AppID ORDER BY `Builds`.`BuildID` DESC LIMIT 1",
                                         new { appID }
                                         )).SingleOrDefault();

                        if (build == null)
                        {
                            continue;
                        }

                        if (DateTime.UtcNow > build.Date.AddMinutes(60))
                        {
                            Log.WriteDebug("RSS", "Got {0} update patch notes, but there is no build within last 10 minutes. {1}", appID, item.Link);
                            IRC.Instance.SendOps($"{Colors.GREEN}[Patch notes]{Colors.NORMAL} Got {appID} update patch notes, but there is no build within last 10 minutes. {item.Link}");
                            continue;
                        }

                        if (build.Official > 0)
                        {
                            Log.WriteDebug("RSS", "Got {0} update patch notes, but official patch notes is already filled. {1}", appID, item.Link);
                            IRC.Instance.SendOps($"{Colors.GREEN}[Patch notes]{Colors.NORMAL} Got {appID} update patch notes, but official patch notes is already filled. {item.Link}");
                            continue;
                        }

                        // breaks
                        item.Content = new Regex(@"<br( \/)?>\r?\n?", RegexOptions.Multiline | RegexOptions.CultureInvariant).Replace(item.Content, "\n");

                        // dashes (CS:GO mainly)
                        item.Content = new Regex("^&#(8208|8209|8210|8211|8212|8213); ?", RegexOptions.Multiline | RegexOptions.CultureInvariant).Replace(item.Content, "* ");

                        // add source
                        item.Content = string.Format("Via [{0}]({1}):\n\n{2}", appID == 730 ? "CS:GO Blog" : "the Steam Store", item.Link, item.Content);

                        Log.WriteDebug("RSS", "Inserting {0} patchnotes for build {1}:\n{2}", build.AppID, build.BuildID, item.Content);

                        await db.ExecuteAsync(
                            "INSERT INTO `Patchnotes` (`BuildID`, `AppID`, `ChangeID`, `Date`, `Official`) " +
                            "VALUES (@BuildID, @AppID, @ChangeID, @Date, @Content) ON DUPLICATE KEY UPDATE `Official` = VALUES(`Official`), `LastEditor` = @AccountID",
                            new
                        {
                            build.BuildID,
                            build.AppID,
                            build.ChangeID,
                            Date = build.Date.AddSeconds(1).ToString("yyyy-MM-dd HH:mm:ss"),
                            item.Content,
                            Steam.Instance.Client.SteamID.AccountID
                        }
                            );

                        IRC.Instance.SendMain($"\u2699 Official patch notes:{Colors.BLUE} {Steam.GetAppName(build.AppID)}{Colors.NORMAL} -{Colors.DARKBLUE} {SteamDB.GetPatchnotesURL(build.BuildID)}");
                    }
                }
            }
        }
Пример #27
0
        public override async Task OnCommand(CommandArguments command)
        {
            if (string.IsNullOrWhiteSpace(command.Message))
            {
                command.Reply($"Usage:{Colors.OLIVE} app <appid or partial game name>");

                return;
            }

            string name;

            if (!uint.TryParse(command.Message, out var appID))
            {
                appID = await TrySearchAppId(command);

                if (appID == 0)
                {
                    return;
                }
            }

            var tokenTask = Steam.Instance.Apps.PICSGetAccessTokens(appID, null);

            tokenTask.Timeout = TimeSpan.FromSeconds(10);
            var tokenCallback = await tokenTask;

            SteamApps.PICSRequest request;

            if (tokenCallback.AppTokens.ContainsKey(appID))
            {
                request = PICSTokens.NewAppRequest(appID, tokenCallback.AppTokens[appID]);
            }
            else
            {
                request = PICSTokens.NewAppRequest(appID);
            }

            var infoTask = Steam.Instance.Apps.PICSGetProductInfo(new List <SteamApps.PICSRequest> {
                request
            }, Enumerable.Empty <SteamApps.PICSRequest>());

            infoTask.Timeout = TimeSpan.FromSeconds(10);
            var job      = await infoTask;
            var callback = job.Results?.FirstOrDefault(x => x.Apps.ContainsKey(appID));

            if (callback == null)
            {
                command.Reply($"Unknown AppID: {Colors.BLUE}{appID}{(LicenseList.OwnedApps.ContainsKey(appID) ? SteamDB.StringCheckmark : string.Empty)}");

                return;
            }

            var info = callback.Apps[appID];

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

            info.KeyValues.SaveToFile(Path.Combine(Application.Path, "app", $"{info.ID}.vdf"), false);

            command.Reply($"{Colors.BLUE}{name}{Colors.NORMAL} -{Colors.DARKBLUE} {SteamDB.GetAppUrl(info.ID)}{Colors.NORMAL} - Dump:{Colors.DARKBLUE} {SteamDB.GetRawAppUrl(info.ID)}{Colors.NORMAL}{(info.MissingToken ? SteamDB.StringNeedToken : string.Empty)}{(LicenseList.OwnedApps.ContainsKey(info.ID) ? SteamDB.StringCheckmark : string.Empty)}");

            if (command.IsUserAdmin && !LicenseList.OwnedApps.ContainsKey(info.ID))
            {
                JobManager.AddJob(() => Steam.Instance.Apps.RequestFreeLicense(info.ID));
            }
        }
Пример #28
0
        private bool ProcessKey(string keyName, string displayName, string value, bool isJSON = false)
        {
            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.ToLower().Trim();

            if (!CurrentData.ContainsKey(keyName))
            {
                uint key = GetKeyNameID(keyName);

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

                    DbConnection.Execute("INSERT INTO `KeyNames` (`Name`, `Type`, `DisplayName`) VALUES(@Name, @Type, @DisplayName)", new { Name = keyName, DisplayName = displayName, Type = type });

                    key = GetKeyNameID(keyName);

                    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"));
                }

                DbConnection.Execute("INSERT INTO `AppsInfo` (`AppID`, `Key`, `Value`) VALUES (@AppID, @Key, @Value)", new { AppID, Key = key, Value = value });
                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.Equals(value))
            {
                DbConnection.Execute("UPDATE `AppsInfo` SET `Value` = @Value WHERE `AppID` = @AppID AND `Key` = @Key", new { AppID, Key = data.Key, Value = value });
                MakeHistory("modified_key", data.Key, data.Value, value);

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

                return(true);
            }

            return(false);
        }
Пример #29
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)
                                                  );
                        }
                    }
                });
            }
        }
Пример #30
0
        private void PrintLinux()
        {
            string appType;
            var    name = Steam.GetAppName(AppID, out appType);

            if (appType != "Game" && appType != "Application")
            {
                return;
            }

            IRC.Instance.SendSteamLUG(string.Format(
                                          "[oslist][{0}] {1} now lists Linux{2} - {3} - https://store.steampowered.com/app/{4}/",
                                          appType, name, LinkExpander.GetFormattedPrices(AppID), SteamDB.GetAppURL(AppID, "history"), AppID
                                          ));
        }