示例#1
0
        public static async Task Anagram(CommandContext bc)
        {
            if (bc.MessageTokens.Length == 1)
            {
                await bc.SendReply("Syntax: !Anagram <anagram>");
                return;
            }

            string query = bc.MessageTokens.Join(1);

            using (var clueFile = new StreamReader("Data/Clues.txt"))
            {
                string clueLine;
                while ((clueLine = clueFile.ReadLine()) != null)
                {
                    if (!clueLine.StartsWith("Anagram", StringComparison.Ordinal))
                    {
                        continue;
                    }
                    string[] clueTokens = clueLine.Split('|');
                    if (clueTokens[1].ContainsI(query))
                    {
                        await bc.SendReply(@"Anagram: \c07{0}\c | NPC: \c07{1}\c | Location: \c07{2}\c", clueTokens[1], clueTokens[2], clueTokens[3]);
                        return;
                    }
                }
            }
            await bc.SendReply(@"Could not locate \c07'{0}'\c anagram.", query);
        }
示例#2
0
        public static async Task Remove(CommandContext bc)
        {
            if (!bc.IsAdmin)
            {
                return;
            }

            if (bc.MessageTokens.Length <= 1)
            {
                await bc.SendReply("Syntax: !RemoveTracker <rsn>");
                return;
            }

            string rsn = bc.MessageTokens.Join(1).ValidatePlayerName();
            var playerId = await Database.Lookup("id", "players", "rsn LIKE @name", new[] { new MySqlParameter("@name", rsn) }, -1L);
            if (playerId != -1)
            {
                await Database.ExecuteNonQuery("DELETE FROM tracker WHERE pid=" + playerId);
                await Database.ExecuteNonQuery("DELETE FROM players WHERE id=" + playerId);
                await bc.SendReply(@"\b{0}\b was removed from the tracker database.", rsn);
            }
            else
            {
                await bc.SendReply(@"\b{0}\b was not found on the tracker database.", rsn);
            }
        }
示例#3
0
        public static async Task WarEnd(CommandContext bc)
        {
            if (!bc.IsAdmin)
            {
                await bc.SendReply("You need to be a bot administrator to use this command.");
                return;
            }

            // get channel name
            string channelName = bc.Channel;
            Match matchChannel = Regex.Match(bc.Message, @"#(\S+)");
            if (matchChannel.Success)
            {
                channelName = matchChannel.Groups[1].Value;
                bc.Message = bc.Message.Replace(matchChannel.Value, string.Empty);
            }
            var channelNameParameter = new MySqlParameter("@channelName", channelName);

            // get skill name
            var skillName = await Database.Lookup<string>("skill", "wars", "channel=@channelName", new[] { channelNameParameter });
            if (skillName == null)
            {
                await bc.SendReply("You have to start a war in this channel first using !WarStart <skill>.");
                return;
            }

            string reply = string.Empty;
            var warPlayers = await Database.FetchAll("SELECT rsn FROM warPlayers WHERE channel=@channel", new MySqlParameter("@channel", channelName));
            for (int count = 1; count <= warPlayers.Count; count++)
            {
                var p = await Player.FromHiscores(warPlayers[count - 1].GetString(0));
                if (!p.Ranked)
                {
                    await bc.SendReply(@"Player \b" + p.Name + "\b has changed his/her name or was banned during the war, and couldn't be tracked.");
                    continue;
                }
                if (count % 2 == 0)
                {
                    reply += @"\c07{0} ({1:e});\c ".FormatWith(p.Name, p.Skills[skillName]);
                }
                else
                {
                    reply += "{0} ({1:e}); ".FormatWith(p.Name, p.Skills[skillName]);
                }
                if (count % 5 == 0)
                {
                    await bc.SendReply(reply);
                    reply = string.Empty;
                }
            }
            if (!string.IsNullOrEmpty(reply))
            {
                await bc.SendReply(reply);
            }

            await bc.SendReply(@"\b{0}\b war ended on \u{1}\u for these players.", skillName, DateTime.UtcNow);

            await Database.ExecuteNonQuery("DELETE FROM wars WHERE channel='" + channelName + "'");
            await Database.ExecuteNonQuery("DELETE FROM warPlayers WHERE channel='" + channelName + "'");
        }
示例#4
0
        public static async Task SlayerPercent(CommandContext bc)
        {
            // get rsn
            string rsn;
            if (bc.MessageTokens.Length > 1)
            {
                rsn = await bc.GetPlayerName(bc.MessageTokens.Join(1));
            }
            else
            {
                rsn = await bc.GetPlayerName(bc.From.Nickname);
            }

            var p = await Player.FromHiscores(rsn);
            if (!p.Ranked)
            {
                await bc.SendReply(@"\b{0}\b doesn't feature Hiscores.", rsn);
                return;
            }

            long hits_exp_gained = p.Skills[Skill.HITP].Exp - 1154;
            double expected_max_slayer_exp = hits_exp_gained * 3.0 / 4.0;

            await bc.SendReply(@"\b{0}\b \c07{1:0.##}% - {2:0.##}%\c of combat exp. is slayer based, with \c07{3:N0}\c combat slayer exp. and \c07{4:N0}\c combat total exp. (This percentage isn't accurate, mostly because of monster hp regeneration ratio and cannon slayering.)", rsn, (double) p.Skills[Skill.SLAY].Exp / expected_max_slayer_exp * 100.0, (double) p.Skills[Skill.SLAY].Exp / (expected_max_slayer_exp - ((double) hits_exp_gained / 133.0)) * 100.0, (double) p.Skills[Skill.SLAY].Exp * 16.0 / 3.0, hits_exp_gained + hits_exp_gained * 3);
        }
示例#5
0
        public static async Task Search(CommandContext bc)
        {
            if (bc.MessageTokens.Length == 1)
            {
                await bc.SendReply("Syntax: !MonsterSearch <search terms>");
                return;
            }

            // get search terms
            string search_terms = bc.MessageTokens.Join(1);

            var results = new Monsters(search_terms);

            if (results.Count > 0)
            {
                var reply = @"\c12www.zybez.net\c found \c07{0}\c results:".FormatWith(results.Count);
                for (int i = 0; i < Math.Min(15, results.Count); i++)
                {
                    reply += @" \c07{0}\c ({1});".FormatWith(results[i].Name, results[i].Level);
                }
                await bc.SendReply(reply);
            }
            else
            {
                await bc.SendReply(@"\c12www.zybez.net\c doesn't have any record for '{0}'.", search_terms);
            }
        }
示例#6
0
        public static async Task Add(CommandContext bc)
        {
            if (!bc.IsAdmin)
            {
                return;
            }

            if (bc.MessageTokens.Length <= 1)
            {
                await bc.SendReply("Syntax: !AddTracker <rsn>");
                return;
            }

            string rsn = bc.MessageTokens.Join(1).ValidatePlayerName();
            try
            {
                var p = await Player.FromHiscores(rsn);
                if (p.Ranked)
                {
                    await Database.Insert("players", "rsn", rsn, "clan", string.Empty, "lastupdate", string.Empty);
                    await p.SaveToDB(DateTime.UtcNow.ToStringI("yyyyMMdd"));
                    await bc.SendReply(@"\b{0}\b is now being tracked.", rsn);
                }
                else
                {
                    await bc.SendReply(@"\b{0}\b doesn't feature Hiscores.", rsn);
                }
                return;
            }
            catch
            {
            }
            await bc.SendReply(@"\b{0}\b was already being tracked.", rsn);
        }
示例#7
0
        public static async Task Percent(CommandContext bc)
        {
            // get rsn
            var rsn = await bc.GetPlayerName(bc.MessageTokens.Length > 1 ? bc.MessageTokens.Join(1) : bc.From.Nickname);

            var p = await Player.FromHiscores(rsn);
            if (!p.Ranked)
            {
                await bc.SendReply(@"\b{0}\b doesn't feature Hiscores.", rsn);
                return;
            }

            long totalExp = p.Skills[Skill.OVER].Exp;
            long combatExp = p.Skills[Skill.COMB].Exp;
            long f2pExp = p.Skills.F2pExp;

            // slayer
            long hits_exp_gained = p.Skills[Skill.HITP].Exp - 1154;
            var expected_max_slayer_exp = (int) (hits_exp_gained * 3.0 / 4.0);

            // pc
            long expected_combat_xp = p.Skills[Skill.HITP].Exp + p.Skills[Skill.HITP].Exp * 12 / 4;
            long current_combat_xp = p.Skills[Skill.HITP].Exp + p.Skills[Skill.ATTA].Exp + p.Skills[Skill.STRE].Exp + p.Skills[Skill.DEFE].Exp + p.Skills[Skill.RANG].Exp;

            await bc.SendReply(@"\b{0}\b statistic percentages | Total exp: \c07{1:N0}\c | Combat exp: \c07{2:N0}\c (\c07{3:0.##}%\c) | F2P exp: \c07{4:N0}\c (\c07{5:0.##}%\c) | Slayer%: \c07{6:0.##}% - {7:0.##}%\c | PestControl%: \c07{8:0.##}%\c", rsn, totalExp, combatExp, (double) combatExp / totalExp * 100, f2pExp, (double) f2pExp / totalExp * 100, (double) p.Skills[Skill.SLAY].Exp / expected_max_slayer_exp * 100, (double) p.Skills[Skill.SLAY].Exp / (expected_max_slayer_exp - (hits_exp_gained / 133)) * 100, (double) (current_combat_xp - expected_combat_xp) / current_combat_xp * 100);
        }
示例#8
0
        public static async Task Start(CommandContext bc)
        {
            // get rsn
            string rsn = await bc.GetPlayerName(bc.From.Nickname);

            var p = await Player.FromHiscores(rsn);
            if (!p.Ranked)
            {
                await bc.SendReply(@"\b{0}\b doesn't feature Hiscores.", rsn);
                return;
            }

            // get timer name
            string name = string.Empty;
            int indexOfSharp = bc.Message.IndexOf('#');
            if (indexOfSharp > 0)
            {
                name = bc.Message.Substring(indexOfSharp + 1);
                bc.Message = bc.Message.Substring(0, indexOfSharp - 1);
            }

            // get skill
            string skill = Skill.OVER;
            if (bc.MessageTokens.Length > 1)
            {
                Skill.TryParse(bc.MessageTokens[1], ref skill);
            }

            // remove previous timer with this name, if any
            await Database.ExecuteNonQuery("DELETE FROM timers_exp WHERE fingerprint='" + bc.From.FingerPrint + "' AND name='" + name.Replace("'", "''") + "'");

            // start a new timer with this name
            await Database.Insert("timers_exp", "fingerprint", bc.From.FingerPrint, "name", name, "skill", skill, "exp", p.Skills[skill].Exp.ToStringI(), "datetime", DateTime.UtcNow.ToStringI("yyyyMMddHHmmss"));
            await bc.SendReply(@"\b{0}\b starting exp of \c07{1:e}\c in \u{1:n}\u has been recorded{2}.", rsn, p.Skills[skill], name.Length > 0 ? @" on timer \c07" + name + @"\c" : string.Empty);
        }
示例#9
0
        public static async Task Set(CommandContext bc)
        {
            if (bc.MessageTokens.Length < 2)
            {
                await bc.SendReply("Syntax: !set <param> [skill] <value>");
                return;
            }

            switch (bc.MessageTokens[1].ToUpperInvariant())
            {
                case "RSN":
                case "NAME":
                    await _SetName(bc);
                    break;
                case "GOAL":
                    await _SetGoal(bc);
                    break;
                case "ITEM":
                    await _SetItem(bc);
                    break;
                case "SPEED":
                    await _SetSpeed(bc);
                    break;
                case "SKILL":
                    await _SetSkill(bc);
                    break;
                case "@":
                    await _SetSkillToggle(bc);
                    break;
                default:
                    await bc.SendReply("Error: Unknown parameter.");
                    break;
            }
        }
示例#10
0
        public static async Task Timer(CommandContext bc)
        {
            if (bc.MessageTokens.Length == 1)
            {
                int timers = 0;
                string reply = string.Empty;
                foreach (var rsTimer in await Database.FetchAll("SELECT name,duration,started FROM timers WHERE fingerprint=@fingerprint OR nick=@nick", new MySqlParameter("@fingerprint", bc.From.FingerPrint), new MySqlParameter("@nick", bc.From.Nickname)))
                {
                    timers++;
                    DateTime start = rsTimer.GetString(2).ToDateTime();
                    DateTime end = start.AddSeconds(rsTimer.GetDouble(1));
                    reply += @" \b#{0}\b timer (\c07{1}\c) ends in \c07{2}\c, at \c07{3}\c;".FormatWith(timers, rsTimer.GetString(0), (end - DateTime.UtcNow).ToLongString(), end.ToStringI("yyyy/MM/dd HH:mm:ss"));
                }
                if (timers > 0)
                {
                    await bc.SendReply(@"Found \c07{0}\c timers: {1}", timers, reply);
                }
                else
                {
                    await bc.SendReply("Syntax: !timer <duration>");
                }

                return;
            }

            // get duration
            int duration;
            string name;
            switch (bc.MessageTokens[1].ToUpperInvariant())
            {
                case "FARM":
                case "HERB":
                case "HERBS":
                    duration = 75;
                    name = bc.MessageTokens[1].ToLowerInvariant();
                    break;
                case "DAY":
                    duration = 1440;
                    name = bc.MessageTokens[1].ToLowerInvariant();
                    break;
                case "WEEK":
                case "TOG":
                    duration = 10080;
                    name = bc.MessageTokens[1].ToLowerInvariant();
                    break;
                default:
                    if (!int.TryParse(bc.MessageTokens[1], out duration))
                    {
                        await bc.SendReply("Error: Invalid duration. Duration must be in minutes.");
                        return;
                    }
                    name = duration + " mins";
                    break;
            }

            // start a new timer for this duration
            await Database.Insert("timers", "fingerprint", bc.From.FingerPrint, "nick", bc.From.Nickname, "name", name, "duration", (duration * 60).ToStringI(), "started", DateTime.UtcNow.ToStringI("yyyyMMddHHmmss"));
            await bc.SendReply(@"Timer started to \b{0}\b. Timer will end at \c07{1}\c.", bc.From.Nickname, DateTime.UtcNow.AddMinutes(duration).ToStringI("yyyy/MM/dd HH:mm:ss"));
        }
示例#11
0
        public static async Task Event(CommandContext bc)
        {
            bool all = bc.Message.Contains("@all");
            try
            {
                string eventPage = new WebClient().DownloadString("http://supremeskillers.net/api/?module=events&action=getNext");
                JObject nextEvent = JObject.Parse(eventPage);

                if (nextEvent["data"] == null)
                {
                    await bc.SendReply(@"No events currently set for Supreme Skillers. \c12http://supremeskillers.net/forum/public-events/\c");
                    return;
                }

                var events = new string[10];
                int i = -1;
                foreach (JObject eventData in nextEvent["data"])
                {
                    events[++i] = eventData.ToString();
                }

                DateTime startTime;
                if (!all)
                {
                    nextEvent = JObject.Parse(events[0]);
                    startTime = DateTime.ParseExact((string) nextEvent["startTime"], "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
                    await bc.SendReply(@"Next event: \c07{0}\c starts in \c07{1}\c for more information: \c12{2}\c", nextEvent["description"], (startTime - DateTime.UtcNow).ToLongString(), nextEvent["url"]);
                }
                else
                {
                    string reply = "Upcoming SS Events: ";
                    for (i = 0; i < 10; i++)
                    {
                        if (events[i] == null)
                        {
                            break;
                        }
                        nextEvent = JObject.Parse(events[i]);
                        startTime = DateTime.ParseExact((string) nextEvent["startTime"], "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
                        if (i == 0)
                        {
                            reply += @"{0} (\c07{1}\c); ".FormatWith(nextEvent["description"], (startTime - DateTime.UtcNow).ToLongString());
                        }
                        else
                        {
                            reply += @"{0} (\c07{1}\c); ".FormatWith(nextEvent["description"], string.Format("{0: dddd d MMMM}", startTime).Trim());
                        }
                    }
                    await bc.SendReply(reply.Trim());
                }
                return;
            }
            catch
            {
            }
            await bc.SendReply("Error retrieving next event.");
        }
示例#12
0
        public static async Task ClanCheck(CommandContext bc)
        {
            if (!bc.IsAdmin)
            {
                await bc.SendReply("You need to be a bot administrator to use this command.");
                return;
            }

            if (bc.MessageTokens.Length == 1)
            {
                await bc.SendReply("Syntax: !ClanCheck <runehead alias>");
            }

            // get @f2p
            bool f2p = false;
            if (bc.Message.Contains(" @f2p"))
            {
                f2p = true;
                bc.Message = bc.Message.Replace(" @f2p", string.Empty);
            }

            // get @p2p
            bool p2p = false;
            if (bc.Message.Contains(" @p2p"))
            {
                p2p = true;
                bc.Message = bc.Message.Replace(" @p2p", string.Empty);
            }

            string pageRuneHead = new WebClient().DownloadString("http://runehead.com/clans/ml.php?sort=name&clan=" + bc.MessageTokens[1]);
            foreach (Match clanMember in Regex.Matches(pageRuneHead, "\\?name=([^&]+)"))
            {
                var p = await Player.FromHiscores(clanMember.Groups[1].Value.ValidatePlayerName());
                if (!p.Ranked)
                {
                    await bc.SendReply(@"\b{0}\b is not ranked.", p.Name);
                    continue;
                }
                if (p.Name.StartsWithI("_") || p.Name.EndsWithI("_"))
                {
                    await bc.SendReply(@"\b{0}\b has unneeded underscores. Please change it to \b{1}\c.", p.Name, p.Name.Trim('_'));
                }

                if (f2p && p.Skills.F2pExp == p.Skills[Skill.OVER].Exp)
                {
                    await bc.SendReply(@"\b{0}\b is \c14F2P\c.", p.Name);
                }

                if (p2p && p.Skills.F2pExp != p.Skills[Skill.OVER].Exp)
                {
                    await bc.SendReply(@"\b{0}\b is \c07P2P\c.", p.Name);
                }
            }

            await bc.SendReply(@"Clan \b{0}\b is checked.", bc.MessageTokens[1]);
        }
示例#13
0
        public static async Task WarAdd(CommandContext bc)
        {
            if (!bc.IsAdmin)
            {
                await bc.SendReply("You need to be a bot administrator to use this command.");
                return;
            }

            if (bc.MessageTokens.Length < 2)
            {
                await bc.SendReply(@"\bSyntax:\b !WarAdd <player name> [#channel name]");
                return;
            }

            // get channel name
            string channelName = bc.Channel;
            Match matchChannel = Regex.Match(bc.Message, @"#(\S+)");
            if (matchChannel.Success)
            {
                channelName = matchChannel.Groups[1].Value;
                bc.Message = bc.Message.Replace(matchChannel.Value, string.Empty);
            }
            var channelNameParameter = new MySqlParameter("@channelName", channelName);

            string[] playerNames = bc.MessageTokens.Join(1).Split(new[] { ',', ';', '+', '|' });
            foreach (string playerName in playerNames.Select(name => name.ValidatePlayerName()))
            {
                if (await Database.Lookup<string>("rsn", "warPlayers", "channel=@channelName", new[] { channelNameParameter }) == playerName)
                {
                    await bc.SendReply(@"\b{0}\b is already signed to current war.", playerName);
                }
                else
                {
                    var player = await Player.FromHiscores(playerName);
                    if (player.Ranked)
                    {
                        var skillName = await Database.Lookup<string>("skill", "wars", "channel=@channelName", new[] { channelNameParameter });
                        if (skillName == null)
                        {
                            await Database.Insert("warPlayers", "channel", channelName, "rsn", playerName);
                        }
                        else
                        {
                            await Database.Insert("warPlayers", "channel", channelName, "rsn", playerName, "startLevel", player.Skills[skillName].Level.ToStringI(), "startExp", player.Skills[skillName].Exp.ToStringI(), "startRank", player.Skills[skillName].Rank.ToStringI());
                        }
                        await bc.SendReply(@"\b{0}\b is now signed to current war.", playerName);
                        await Task.Delay(1000);
                    }
                    else
                    {
                        await bc.SendReply(@"\b{0}\b doesn't feature hiscores.", playerName);
                    }
                }
            }
        }
示例#14
0
        public static async Task WarStart(CommandContext bc)
        {
            if (!bc.IsAdmin)
            {
                await bc.SendReply("You need to be a bot administrator to use this command.");
                return;
            }

            // get channel name
            string channelName = bc.Channel;
            Match matchChannel = Regex.Match(bc.Message, @"#(\S+)");
            if (matchChannel.Success)
            {
                channelName = matchChannel.Groups[1].Value;
                bc.Message = bc.Message.Replace(matchChannel.Value, string.Empty);
            }

            // get skill name
            string skillName = Skill.OVER;
            if (bc.MessageTokens.Length < 2 || !Skill.TryParse(bc.MessageTokens[1], ref skillName))
            {
                await bc.SendReply(@"\bSyntax:\b !WarStart <skill name> [#channel name]");
                return;
            }

            string reply = string.Empty;
            var warPlayers = await Database.FetchAll("SELECT rsn FROM warPlayers WHERE channel=@channel", new MySqlParameter("@channel", channelName));
            for (int count = 1; count <= warPlayers.Count; count++)
            {
                var p = await Player.FromHiscores(warPlayers[count - 1].GetString(0));
                await Database.Update("warPlayers", "channel='" + channelName + "' AND rsn='" + p.Name + "'", "startLevel", p.Skills[skillName].Level.ToStringI(), "startExp", p.Skills[skillName].Exp.ToStringI(), "startRank", p.Skills[skillName].Rank.ToStringI());
                if (count % 2 == 0)
                {
                    reply += @"\c07{0} ({1:e});\c ".FormatWith(p.Name, p.Skills[skillName]);
                }
                else
                {
                    reply += "{0} ({1:e}); ".FormatWith(p.Name, p.Skills[skillName]);
                }
                if (count % 5 == 0)
                {
                    await bc.SendReply(reply);
                    reply = string.Empty;
                }
            }
            if (!string.IsNullOrEmpty(reply))
            {
                await bc.SendReply(reply);
            }

            await Database.ExecuteNonQuery("DELETE FROM wars WHERE channel='" + channelName + "'");
            await Database.Insert("wars", "channel", channelName, "skill", skillName, "startDate", DateTime.UtcNow.ToStringI("yyyyMMddHHmm"));

            await bc.SendReply(@"\b{0}\b war started on \u{1}\u for these players. \bYou can now login and good luck!\b", skillName, DateTime.UtcNow);
        }
示例#15
0
        public static async Task Item(CommandContext bc)
        {
            if (bc.MessageTokens.Length == 1)
            {
                await bc.SendReply("Syntax: !Item <item>");
                return;
            }

            Item item;

            int itemId;
            if (int.TryParse(bc.MessageTokens[1].TrimStart('#'), out itemId))
            {
                // !Item <id>
                item = new Item(itemId);
            }
            else
            {
                string query = bc.MessageTokens.Join(1);
                var items = new Items(query);

                switch (items.Count)
                {
                    case 0:
                        await bc.SendReply(@"\c12www.zybez.net\c doesn't have any record for item '{0}'.", query);
                        return;
                    case 1:
                        item = items[0];
                        item.LoadFromWeb();
                        break;
                    default:
                        string reply = @"\c12www.zybez.net\c has \c07{0}\c items:".FormatWith(items.Count);
                        for (int i = 0; i < Math.Min(14, items.Count); i++)
                        {
                            reply += @" \c07#{0}\c {1};".FormatWith(items[i].Id, items[i].Name);
                        }
                        if (items.Count > 14)
                        {
                            reply += " ...";
                        }
                        await bc.SendReply(reply);
                        return;
                }
            }

            if (item.Name == null)
            {
                await bc.SendReply(@"\c12www.zybez.net\c doesn't have any record for item \c07#{0}\c.", itemId);
            }
            else
            {
                await bc.SendReply(@"\c07{0}\c | Alch: \c07{1}/{2}\c | MarketPrice: \c07{3}\c | Location: \c07{4}\c | \c12www.zybez.net/item.aspx?id={5}\c", item.Name, item.HighAlch.ToShortString(1), item.LowAlch.ToShortString(1), item.MarketPrice.ToShortString(1), item.Location, item.Id);
                await bc.SendReply(@"Members? \c{0}\c | Quest: \c{1}\c | Tradeable? \c{2}\c | Stackable? \c{3}\c | Weight? \c07{4}Kg\c | Examine: \c07{5}\c", item.Members ? "3Yes" : "4No", string.IsNullOrEmpty(item.Quests) ? "3Yes" : "4No", item.Tradable ? "3Yes" : "4No", item.Stackable ? "3Yes" : "4No", item.Weight, item.Examine);
            }
        }
示例#16
0
        public static async Task Calc(CommandContext bc)
        {
            if (bc.MessageTokens.Length == 1)
            {
                await bc.SendReply("Syntax: !calc <expression>");
                return;
            }

            var mp = new MathParser();
            mp.Evaluate(bc.MessageTokens.Join(1));
            await bc.SendReply(@"\c07{0}\c => \c07{1}\c", mp.Expression, mp.ValueAsString);
        }
示例#17
0
 public static async Task Effigies(CommandContext bc)
 {
     // .effigy <lvl>
     int level;
     if (bc.MessageTokens.Length != 2 || bc.MessageTokens[1].TryInt32(out level) == false)
     {
         await bc.SendReply("Syntax: !effigy <level>");
         return;
     }
     var exp = (int) ((Math.Pow(level, 3) - 2 * Math.Pow(level, 2) + 100 * level) / 20);
     string reply = @"A Dragonkin Lamp used on a skill of Level \c07{0}\c will give \c07{1:N0}\c Experience.".FormatWith(level, exp);
     await bc.SendReply(reply);
 }
示例#18
0
        public static async Task WarTopAll(CommandContext bc)
        {
            if (!bc.IsAdmin)
            {
                await bc.SendReply("You need to be a bot administrator to use this command.");
                return;
            }

            var skill = await Database.Lookup<string>("skill", "wars", "channel=@chan", new[] { new MySqlParameter("@chan", bc.Channel) });
            if (skill == null)
            {
                await bc.SendReply("There isn't a war going on in this channel.");
                return;
            }

            await bc.SendReply("Please wait while the bot gathers all players stats...");

            // Create a list of the war players
            var warPlayers = new List<Player>();
            foreach (var warPlayersDr in await Database.FetchAll("SELECT rsn,startrank,startlevel,startexp FROM warplayers WHERE channel=@channel", new MySqlParameter("@channel", bc.Channel)))
            {
                var warPlayer = await Player.FromHiscores(warPlayersDr.GetString(0));
                if (!warPlayer.Ranked)
                {
                    continue;
                }
                warPlayer.Skills[skill] -= new Skill(skill, warPlayersDr.GetInt32(1), warPlayersDr.GetInt32(2), warPlayersDr.GetInt32(3));
                warPlayers.Add(warPlayer);
            }
            warPlayers = warPlayers.OrderByDescending(p => p.Skills[skill].Exp).ToList();

            string reply = null;
            for (int i = 0; i < warPlayers.Count; i++)
            {
                if (i % 5 == 0)
                {
                    if (reply != null)
                    {
                        await bc.SendReply(reply);
                    }
                    reply = @"War \u{0}\u ranking:".FormatWith(skill.ToLowerInvariant());
                }
                reply += @" \c07#{0}\c {1} ({2:e});".FormatWith(i + 1, warPlayers[i].Name, warPlayers[i].Skills[skill]);
            }
            if (reply != null)
            {
                await bc.SendReply(reply);
            }
        }
示例#19
0
        public static async Task Check(CommandContext bc)
        {
            // get rsn
            string rsn = await bc.GetPlayerName(bc.From.Nickname);

            var p = await Player.FromHiscores(rsn);
            if (!p.Ranked)
            {
                await bc.SendReply(@"\b{0}\b doesn't feature Hiscores.", rsn);
                return;
            }

            // get timer name
            string name = string.Empty;
            int indexofsharp = bc.Message.IndexOf('#');
            if (indexofsharp > 0)
            {
                name = bc.Message.Substring(indexofsharp + 1);
                bc.Message = bc.Message.Substring(0, indexofsharp - 1);
            }

            var rs = await Database.FetchFirst("SELECT skill,exp,datetime FROM timers_exp WHERE fingerprint=@fingerprint AND name=@name LIMIT 1", new MySqlParameter("@fingerprint", bc.From.FingerPrint), new MySqlParameter("@name", name));
            if (rs != null)
            {
                string skill = rs.GetString(0);

                long gained_exp = p.Skills[skill].Exp - (uint) rs["exp"];
                TimeSpan time = DateTime.UtcNow - rs.GetString(2).ToDateTime();

                var reply = @"You gained \c07{0:N0}\c \u{1}\u exp in \c07{2}\c. That's \c07{3:N0}\c exp/h.".FormatWith(gained_exp, skill.ToLowerInvariant(), time.ToLongString(), (double) gained_exp / time.TotalHours);
                if (gained_exp > 0 && skill != Skill.OVER && skill != Skill.COMB && p.Skills[skill].VLevel < 126)
                {
                    reply += @" Estimated time to level up: \c07{0}\c".FormatWith(TimeSpan.FromSeconds(p.Skills[skill].ExpToVLevel * time.TotalSeconds / gained_exp).ToLongString());
                }
                await bc.SendReply(reply);
            }
            else
            {
                if (name.Length > 0)
                {
                    await bc.SendReply("You must start timing a skill on that timer first.");
                }
                else
                {
                    await bc.SendReply("You must start timing a skill first.");
                }
            }
        }
示例#20
0
        public static async Task Price(CommandContext bc)
        {
            if (bc.MessageTokens.Length == 1)
            {
                await bc.SendReply("Syntax: !Price [qty] <item>");
                return;
            }

            string search_term;
            double qty;
            if (bc.MessageTokens.Length > 2 && MathParser.TryCalc(bc.MessageTokens[1], out qty))
            {
                qty = Math.Round(qty, 1);
                search_term = bc.MessageTokens.Join(2);
            }
            else
            {
                qty = 1;
                search_term = bc.MessageTokens.Join(1);
            }

            var price_list = await Prices.FromRuneScapeSearch(search_term);
            if (price_list.Count == 0)
            {
                await bc.SendReply(@"\c12www.runescape.com\c doesn't have any record for '{0}'.", search_term);
                return;
            }

            var reply = @"\c12www.runescape.com\c found \c07{0}\c results".FormatWith(price_list.TotalItems);
            for (int i = 0; i < Math.Min(10, price_list.Count); i++)
            {
                reply += @" | {0}: \c07{1}\c".FormatWith(price_list[i].Name, (qty * price_list[i].MarketPrice).ToShortString(1));
                if (price_list[i].ChangeToday > 0)
                {
                    reply += @" \c3[+{0}]\c".FormatWith(price_list[i].ChangeToday.ToShortString(1));
                }
                else if (price_list[i].ChangeToday < 0)
                {
                    reply += @" \c4[{0}]\c".FormatWith(price_list[i].ChangeToday.ToShortString(1));
                }
            }
            if (price_list.TotalItems > 10)
            {
                reply += " | (...)";
            }
            await bc.SendReply(reply);
        }
示例#21
0
        public static async Task Qfc(CommandContext bc)
        {
            if (bc.MessageTokens.Length == 1)
            {
                await bc.SendReply("Syntax: !Qfc <qfc>");
                return;
            }

            Match qfc = Regex.Match(bc.MessageTokens.Join(1), "(\\d+).(\\d+).(\\d+).(\\d+)");
            if (!qfc.Success)
            {
                await bc.SendReply("Syntax: !Qfc <qfc>");
            }
            else
            {
                await bc.SendReply(@"Quick find code \c07{0}-{1}-{2}-{3}\c: \c12http://forum.runescape.com/forums.ws?{0},{1},{2},{3}\c", qfc.Groups[1].Value, qfc.Groups[2].Value, qfc.Groups[3].Value, qfc.Groups[4].Value);
            }
        }
示例#22
0
        public static async Task ClanStats(CommandContext bc)
        {
            string skill = Skill.OVER;
            if (bc.MessageTokens.Length > 1)
            {
                Skill.TryParse(bc.MessageTokens[1], ref skill);
            }

            int totallevel = 0;
            long totalexp = 0;
            var ssplayers = await Players.FromClan("SS");
            foreach (Player p in ssplayers)
            {
                totallevel += p.Skills[skill].Level;
                totalexp += p.Skills[skill].Exp;
            }

            await bc.SendReply(@"\bSupreme Skillers\b | Homepage: \c12http://supremeskillers.net\c | \u{0}\u average level: \c07{1}\c (\c07{2:N0}\c average exp.) | Members (\c07{3}\c): \c12http://services.runescape.com/m=clan-hiscores/members.ws?clanId=314\c", skill, totallevel / ssplayers.Count, totalexp / ssplayers.Count, ssplayers.Count);
        }
示例#23
0
        public static async Task Coord(CommandContext bc)
        {
            if (bc.MessageTokens.Length == 1)
            {
                await bc.SendReply("Syntax: !Coords ## ## n/s ## ## w/e");
                return;
            }

            Match M = Regex.Match(bc.Message, @"(\d{1,2})\D*(\d{1,2})[^ns]*([ns])\D*(\d{1,2})\D*(\d{1,2})[^we]*([we])", RegexOptions.IgnoreCase);
            if (M.Success)
            {
                int lat1 = int.Parse(M.Groups[1].Value, CultureInfo.InvariantCulture);
                int lat2 = int.Parse(M.Groups[2].Value, CultureInfo.InvariantCulture);
                char lat = char.ToUpperInvariant(M.Groups[3].Value[0]);
                int lon1 = int.Parse(M.Groups[4].Value, CultureInfo.InvariantCulture);
                int lon2 = int.Parse(M.Groups[5].Value, CultureInfo.InvariantCulture);
                char lon = char.ToUpperInvariant(M.Groups[6].Value[0]);

                using (var clueFile = new StreamReader("Data/Clues.txt"))
                {
                    string clueLine;
                    while ((clueLine = clueFile.ReadLine()) != null)
                    {
                        if (!clueLine.StartsWith("Coords", StringComparison.Ordinal))
                        {
                            continue;
                        }
                        string[] clueTokens = clueLine.Split('|');
                        if (int.Parse(clueTokens[1].Substring(0, 2), CultureInfo.InvariantCulture) == lat1 && int.Parse(clueTokens[1].Substring(2, 2), CultureInfo.InvariantCulture) == lat2 && clueTokens[1][4] == lat && int.Parse(clueTokens[1].Substring(5, 2), CultureInfo.InvariantCulture) == lon1 && int.Parse(clueTokens[1].Substring(7, 2), CultureInfo.InvariantCulture) == lon2 && clueTokens[1][9] == lon)
                        {
                            await bc.SendReply(@"Lat: \c07{0}º{1}'{2}\c | Lon: \c07{3}º{4}'{5}\c | Location: \c07{6}\c (\c12http://www.tip.it/runescape/img2/{0:00}_{1:00}{2}_{3:00}_{4:00}{5}.gif\c)", lat1, lat2, lat.ToStringI().ToUpperInvariant(), lon1, lon2, lon.ToStringI().ToUpperInvariant(), clueTokens[2]);
                            return;
                        }
                    }
                }
                await bc.SendReply(@"Could not locate \c07{0}º{1}'{2}\c / \c07{3}º{4}'{5}\c.", lat1, lat2, lat, lon1, lon2, lon);
            }
            else
            {
                await bc.SendReply("Syntax: !Coords ## ## n/s ## ## w/e");
            }
        }
示例#24
0
        public static async Task Graph(CommandContext bc)
        {
            string skill = Skill.OVER;
            string rsn = await bc.GetPlayerName(bc.From.Nickname);

            if (bc.MessageTokens.Length > 1)
            {
                if (Skill.TryParse(bc.MessageTokens[1], ref skill))
                {
                    if (bc.MessageTokens.Length > 2)
                    {
                        rsn = await bc.GetPlayerName(bc.MessageTokens.Join(2));
                    }
                }
                else
                {
                    rsn = await bc.GetPlayerName(bc.MessageTokens.Join(1));
                }
            }

            await bc.SendReply(@"\b{0}\b \c07{1}\c graph | level: \c12http://t.rscript.org/graph-{0}.{2}.lvl.png\c | exp: \c12http://t.rscript.org/graph-{0}.{2}.png\c | rank: \c12http://t.rscript.org/graph-{0}.{2}.rank.png\c", rsn, skill.ToLowerInvariant(), Skill.NameToId(skill));
        }
示例#25
0
        public static async Task ClanInfo(CommandContext bc)
        {
            if (bc.MessageTokens.Length == 1)
            {
                await bc.SendReply("Syntax: !ClanInfo <clan name|clan initials>");
            }

            // Get the clan to lookup
            string query = bc.MessageTokens.Join(1);

            try
            {
                string pageClans = new WebClient().DownloadString("http://runehead.com/feeds/lowtech/searchclan.php?type=2&search=" + query);
                List<string[]> clans = pageClans.Split('\n').Select(clan => clan.Split('|')).Where(clanInfo => clanInfo.Length == 16).ToArray().ToList();

                // order by overall, make sure ss gets the top spot :p
                try
                {
                    clans.Sort((c1, c2) => -int.Parse(c1[8]).CompareTo(int.Parse(c2[8])));
                }
                catch
                {
                }

                if (clans.Count > 0)
                {
                    await bc.SendReply(@"[\c07{0}\c] \c07{1}\c (\c12{2}\c) | Members: \c07{3}\c | Avg: Cmb: (F2P: \c07{4}\c | P2P: \c07{5}\c) Hp: \c07{6}\c Magic: \c07{7}\c Ranged: \c07{8}\c Skill Total: \c07{9}\c | \c07{10}\c based (Homeworld \c07{11}\c) | Cape: \c07{12}\c | RuneHead: \c12{13}\c", clans[0][4], clans[0][0], clans[0][1], clans[0][5], clans[0][15], clans[0][6], clans[0][7], clans[0][9], clans[0][10], clans[0][8], clans[0][11], clans[0][14], clans[0][13], clans[0][2]);
                }
                else
                {
                    await bc.SendReply(@"\c12www.runehead.com\c doesn't have any record for \b{0}\b.", query);
                }
                return;
            }
            catch
            {
            }
            await bc.SendReply(@"\c12www.runehead.com\c seems to be down.");
        }
示例#26
0
        private static async Task _SetName(CommandContext bc)
        {
            if (bc.MessageTokens.Length < 3)
            {
                await bc.SendReply("Syntax: !set name <rsn>");
                return;
            }

            string rsn = bc.MessageTokens.Join(2).ValidatePlayerName();

            // add/update to database
            if (await Database.Lookup<long>("COUNT(*)", "users", "fingerprint=@fp", new[] { new MySqlParameter("@fp", bc.From.FingerPrint) }) > 0)
            {
                await Database.Update("users", "fingerprint='" + bc.From.FingerPrint + "'", "rsn", rsn);
            }
            else
            {
                await Database.Insert("users", "fingerprint", bc.From.FingerPrint, "rsn", rsn);
            }

            await bc.SendReply(@"Your default RuneScape name is now \b{0}\b. This RSN is associated with the address \u*!*{1}\u.", rsn, bc.From.FingerPrint);
        }
示例#27
0
        public static async Task F2pPercent(CommandContext bc)
        {
            // get rsn
            string rsn;
            if (bc.MessageTokens.Length > 1)
            {
                rsn = await bc.GetPlayerName(bc.MessageTokens.Join(1));
            }
            else
            {
                rsn = await bc.GetPlayerName(bc.From.Nickname);
            }

            var p = await Player.FromHiscores(rsn);
            if (!p.Ranked)
            {
                await bc.SendReply(@"\b{0}\b doesn't feature Hiscores.", rsn);
                return;
            }

            await bc.SendReply(@"\b{0}\b is \c07{1:0.##}%\c f2p based, with \c07{2:N0}\c f2p based exp. and \c07{3:e}\c total exp.", rsn, (double) p.Skills.F2pExp / (double) p.Skills[Skill.OVER].Exp * 100, p.Skills.F2pExp, p.Skills[Skill.OVER]);
        }
示例#28
0
        public static async Task WarRemove(CommandContext bc)
        {
            if (!bc.IsAdmin)
            {
                await bc.SendReply("You need to be a bot administrator to use this command.");
                return;
            }

            if (bc.MessageTokens.Length < 2)
            {
                await bc.SendReply(@"\bSyntax:\b !WarRemove <player name> [#channel name]");
                return;
            }

            // get channel name
            string channelName = bc.Channel;
            Match matchChannel = Regex.Match(bc.Message, @"#(\S+)");
            if (matchChannel.Success)
            {
                channelName = matchChannel.Groups[1].Value;
                bc.Message = bc.Message.Replace(matchChannel.Value, string.Empty);
            }
            var channelNameParameter = new MySqlParameter("@channelName", channelName);

            string playerName = bc.MessageTokens.Join(1).ValidatePlayerName();
            var playerNameParameter = new MySqlParameter("@playerName", playerName);

            if (await Database.Lookup<string>("rsn", "warPlayers", "channel=@channelName AND rsn=@playerName", new[] { channelNameParameter, playerNameParameter }) != null)
            {
                await Database.ExecuteNonQuery("DELETE FROM warPlayers WHERE channel='" + channelName + "' AND rsn='" + playerName + "'");
                await bc.SendReply(@"\b{0}\b was removed from current war.", playerName);
            }
            else
            {
                await bc.SendReply(@"\b{0}\b isn't signed to current war.", playerName);
            }
        }
示例#29
0
        public static async Task Top(CommandContext bc)
        {
            string rsn;
            string skill = null,
                   activity = null;
            int rank;

            // get @level
            bool level = false;
            if (bc.Message.Contains(" @level") || bc.Message.Contains(" @lvl") || bc.Message.Contains(" @l"))
            {
                level = true;
                bc.Message = bc.Message.Replace(" @level", string.Empty);
                bc.Message = bc.Message.Replace(" @lvl", string.Empty);
                bc.Message = bc.Message.Replace(" @l", string.Empty);
            }

            // Parse command arguments
            if (bc.MessageTokens.Length == 1)
            {
                // !Top
                rank = 1;
                skill = Skill.OVER;
            }
            else if (Bot.Activity.TryParse(bc.MessageTokens[1], ref activity) || Skill.TryParse(bc.MessageTokens[1], ref skill))
            {
                // !Top Skill/Activity
                rank = 1;

                if (bc.MessageTokens.Length > 2)
                {
                    if (int.TryParse(bc.MessageTokens[2], out rank))
                    {
                        // !Top Skill/Activity Rank
                    }
                    else
                    {
                        // !Top Skill/Activity RSN
                        rsn = await bc.GetPlayerName(bc.MessageTokens.Join(2));
                        var p = await Player.FromHiscores(rsn);
                        if (p.Ranked)
                        {
                            if (skill == null)
                            {
                                rank = p.Activities[activity].Rank;
                            }
                            else
                            {
                                rank = p.Skills[skill].Rank;
                            }
                        }
                    }
                }
            }
            else if (int.TryParse(bc.MessageTokens[1], out rank))
            {
                // !Top Rank
                skill = Skill.OVER;
            }
            else
            {
                // !Top RSN
                rank = 1;
                skill = Skill.OVER;
                rsn = await bc.GetPlayerName(bc.MessageTokens.Join(1));
                var p = await Player.FromHiscores(rsn);
                if (p.Ranked)
                {
                    rank = p.Skills[skill].Rank;
                }
            }
            if (rank < 0)
            {
                rank = 1;
            }

            var hiscores = new Hiscores(skill, activity, rank);

            int startIndex = Math.Max(rank - hiscores[0].Rank - 5, 0);
            var reply = @"RS \u{0}\u rankings:".FormatWith(hiscores.Name.ToLowerInvariant());
            if (activity == null)
            {
                for (int i = startIndex; i < Math.Min(startIndex + 12, hiscores.Count); i++)
                {
                    reply += " ";
                    if (hiscores[i].Rank == rank)
                    {
                        reply += @"\b";
                    }

                    if (level)
                    {
                        reply += @"\c07#{0:r}\c {1} ({0:l})".FormatWith((Skill) hiscores[i], hiscores[i].RSN);
                    }
                    else
                    {
                        reply += @"\c07#{0:r}\c {1} ({0:e})".FormatWith((Skill) hiscores[i], hiscores[i].RSN);
                    }

                    if (hiscores[i].Rank == rank)
                    {
                        reply += @"\b";
                    }
                    reply += ";";
                }
            }
            else
            {
                for (int i = startIndex; i < Math.Min(12, hiscores.Count); i++)
                {
                    reply += " ";
                    if (hiscores[i].Rank == rank)
                    {
                        reply += @"\b";
                    }
                    reply += @"\c07#{0}\c {1} ({2})".FormatWith(hiscores[i].Rank, hiscores[i].RSN, ((Activity) hiscores[i]).Score);
                    if (hiscores[i].Rank == rank)
                    {
                        reply += @"\b";
                    }
                    reply += ";";
                }
            }
            await bc.SendReply(reply);
        }
示例#30
0
        public static async Task Track(CommandContext bc)
        {
            // get time
            int intervalTime = 604800;
            string intervalName = "1 week";
            Match interval = Regex.Match(bc.Message, @"@(\d+)?(second|minute|month|hour|week|year|sec|min|day|s|m|h|d|w|y)s?", RegexOptions.IgnoreCase);
            if (interval.Success)
            {
                if (interval.Groups[1].Value.Length > 0)
                {
                    intervalTime = int.Parse(interval.Groups[1].Value, CultureInfo.InvariantCulture);
                }
                else
                {
                    intervalTime = 1;
                }
                if (intervalTime < 1)
                {
                    intervalTime = 1;
                }
                switch (interval.Groups[2].Value)
                {
                    case "second":
                    case "sec":
                    case "s":
                        intervalName = intervalTime + " second" + (intervalTime == 1 ? string.Empty : "s");
                        break;
                    case "minute":
                    case "min":
                    case "m":
                        intervalName = intervalTime + " minute" + (intervalTime == 1 ? string.Empty : "s");
                        intervalTime *= 60;
                        break;
                    case "hour":
                    case "h":
                        intervalName = intervalTime + " hour" + (intervalTime == 1 ? string.Empty : "s");
                        intervalTime *= 3600;
                        break;
                    case "day":
                    case "d":
                        intervalName = intervalTime + " day" + (intervalTime == 1 ? string.Empty : "s");
                        intervalTime *= 86400;
                        break;
                    case "week":
                    case "w":
                        intervalName = intervalTime + " week" + (intervalTime == 1 ? string.Empty : "s");
                        intervalTime *= 604800;
                        break;
                    case "month":
                        intervalName = intervalTime + " month" + (intervalTime == 1 ? string.Empty : "s");
                        intervalTime *= 2629746;
                        break;
                    case "year":
                    case "y":
                        intervalName = intervalTime + " year" + (intervalTime == 1 ? string.Empty : "s");
                        intervalTime *= 31556952;
                        break;
                }
                bc.Message = Regex.Replace(bc.Message, @"@(\d+)?(second|minute|month|hour|week|year|sec|min|day|s|m|h|d|w|y)s?", string.Empty, RegexOptions.IgnoreCase);
                bc.Message = bc.Message.Trim();
            }
            intervalName = "last " + intervalName;

            // get rsn
            string rsn;
            if (bc.MessageTokens.Length > 1)
            {
                rsn = await bc.GetPlayerName(bc.MessageTokens.Join(1));
            }
            else
            {
                rsn = await bc.GetPlayerName(bc.From.Nickname);
            }

            // Get new player
            var PlayerNew = await Player.FromHiscores(rsn);
            if (!PlayerNew.Ranked)
            {
                await bc.SendReply(@"\b{0}\b doesn't feature Hiscores.", rsn);
                return;
            }

            // Get old player
            var PlayerOld = new Player(rsn, intervalTime);
            if (!PlayerOld.Ranked)
            {
                await bc.SendReply(@"\b{0}\b wasn't being tracked on {1}.", rsn, DateTime.UtcNow.AddSeconds(-intervalTime).ToStringI("yyyy-MMM-dd"));
                return;
            }

            // 1st line: overall / combat
            string ReplyMsg = @"\b{0}\b \u{1}\u skills:".FormatWith(rsn, intervalName);
            Skill OverallDif = PlayerNew.Skills[Skill.OVER] - PlayerOld.Skills[Skill.OVER];
            if (OverallDif.Exp <= 0)
            {
                await bc.SendReply(@"No performance for \b{0}\b within this period.", rsn);
            }
            else
            {
                Skill CombatDif = PlayerNew.Skills[Skill.COMB] - PlayerOld.Skills[Skill.COMB];

                string DifLevel = string.Empty;
                if (OverallDif.Level > 0)
                {
                    DifLevel = @" [\b+{0}\b]".FormatWith(OverallDif.Level);
                }
                ReplyMsg += @" \c07Overall\c lvl {0} \c3+{1}\c xp (Avg. hourly exp.: \c07{2}\c)".FormatWith(PlayerNew.Skills[Skill.OVER].Level + DifLevel, OverallDif.Exp.ToShortString(1), (OverallDif.Exp / (intervalTime / 3600.0)).ToShortString(0));
                DifLevel = string.Empty;
                if (CombatDif.Level > 0)
                {
                    DifLevel = @" [\b+{0}\b]".FormatWith(CombatDif.Level);
                }
                ReplyMsg += @"; \c7Combat\c lvl {0} \c03+{1}\c xp (\c07{2}%\c)".FormatWith(PlayerNew.Skills[Skill.COMB].Level + DifLevel, CombatDif.Exp.ToShortString(1), (CombatDif.Exp / (double) OverallDif.Exp * 100.0).ToShortString(1));
                await bc.SendReply(ReplyMsg);

                // 2nd line: skills list
                List<Skill> SkillsDif = (from SkillNow in PlayerNew.Skills.Values
                                         where SkillNow.Name != Skill.OVER && SkillNow.Name != Skill.COMB
                                         select SkillNow - PlayerOld.Skills[SkillNow.Name]).ToList();
                SkillsDif.Sort();

                ReplyMsg = @"\b{0}\b \u{1}\u skills:".FormatWith(rsn, intervalName);
                for (int i = 0; i < 10; i++)
                {
                    if (SkillsDif[i].Exp > 0)
                    {
                        DifLevel = string.Empty;
                        if (SkillsDif[i].Level > 0)
                        {
                            DifLevel = @" [\b+{0}\b]".FormatWith(SkillsDif[i].Level);
                        }
                        ReplyMsg += @" \c07{0}\c lvl {1} \c3+{2}\c xp;".FormatWith(SkillsDif[i].Name, PlayerNew.Skills[SkillsDif[i].Name].Level + DifLevel, SkillsDif[i].Exp.ToShortString(1));
                    }
                }
                await bc.SendReply(ReplyMsg);
            }
        }