Merge branch 'main' of https://github.com/Spectralitree/PluralKit into main
This commit is contained in:
		@@ -60,6 +60,7 @@ namespace PluralKit.Bot
 | 
			
		||||
        public static Command GroupPrivacy = new Command("group privacy", "group <group> privacy <description|icon|visibility|all> <public|private>", "Changes a group's privacy settings");
 | 
			
		||||
        public static Command GroupIcon = new Command("group icon", "group <group> icon [url|@mention]", "Changes a group's icon");
 | 
			
		||||
        public static Command GroupDelete = new Command("group delete", "group <group> delete", "Deletes a group");
 | 
			
		||||
        public static Command GroupFrontPercent = new Command("group frontpercent", "group <group> frontpercent [timespan]", "Shows a group's front breakdown.");
 | 
			
		||||
        public static Command GroupMemberRandom = new Command("group random", "group <group> random", "Shows the info card of a randomly selected member in a group.");
 | 
			
		||||
        public static Command GroupRandom = new Command("random", "random group", "Shows the info card of a randomly selected group in your system.");
 | 
			
		||||
        public static Command Switch = new Command("switch", "switch <member> [member 2] [member 3...]", "Registers a switch");
 | 
			
		||||
@@ -107,7 +108,7 @@ namespace PluralKit.Bot
 | 
			
		||||
        public static Command[] GroupCommandsTargeted =
 | 
			
		||||
        {
 | 
			
		||||
            GroupInfo, GroupAdd, GroupRemove, GroupMemberList, GroupRename, GroupDesc, GroupIcon, GroupPrivacy,
 | 
			
		||||
            GroupDelete, GroupMemberRandom
 | 
			
		||||
            GroupDelete, GroupMemberRandom, GroupFrontPercent
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        public static Command[] SwitchCommands = {Switch, SwitchOut, SwitchMove, SwitchDelete, SwitchDeleteAll};
 | 
			
		||||
@@ -397,6 +398,8 @@ namespace PluralKit.Bot
 | 
			
		||||
                    await ctx.Execute<Groups>(GroupDelete, g => g.DeleteGroup(ctx, target));
 | 
			
		||||
                else if (ctx.Match("avatar", "picture", "icon", "image", "pic", "pfp"))
 | 
			
		||||
                    await ctx.Execute<Groups>(GroupIcon, g => g.GroupIcon(ctx, target));
 | 
			
		||||
                else if (ctx.Match("fp", "frontpercent", "front%", "frontbreakdown"))
 | 
			
		||||
                    await ctx.Execute<Groups>(GroupFrontPercent, g => g.GroupFrontPercent(ctx, target));
 | 
			
		||||
                else if (!ctx.HasNext())
 | 
			
		||||
                    await ctx.Execute<Groups>(GroupInfo, g => g.ShowGroupCard(ctx, target));
 | 
			
		||||
                else
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,8 @@ using Dapper;
 | 
			
		||||
 | 
			
		||||
using Humanizer;
 | 
			
		||||
 | 
			
		||||
using NodaTime;
 | 
			
		||||
 | 
			
		||||
using Myriad.Builders;
 | 
			
		||||
 | 
			
		||||
using PluralKit.Core;
 | 
			
		||||
@@ -428,6 +430,31 @@ namespace PluralKit.Bot
 | 
			
		||||
            await ctx.Reply($"{Emojis.Success} Group deleted.");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
            public async Task GroupFrontPercent(Context ctx, PKGroup target)
 | 
			
		||||
        {
 | 
			
		||||
            await using var conn = await _db.Obtain();
 | 
			
		||||
            
 | 
			
		||||
            var targetSystem = await GetGroupSystem(ctx, target, conn);
 | 
			
		||||
            ctx.CheckSystemPrivacy(targetSystem, targetSystem.FrontHistoryPrivacy);
 | 
			
		||||
 | 
			
		||||
            string durationStr = ctx.RemainderOrNull() ?? "30d";
 | 
			
		||||
            
 | 
			
		||||
            var now = SystemClock.Instance.GetCurrentInstant();
 | 
			
		||||
 | 
			
		||||
            var rangeStart = DateUtils.ParseDateTime(durationStr, true, targetSystem.Zone);
 | 
			
		||||
            if (rangeStart == null) throw Errors.InvalidDateTime(durationStr);
 | 
			
		||||
            if (rangeStart.Value.ToInstant() > now) throw Errors.FrontPercentTimeInFuture;
 | 
			
		||||
 | 
			
		||||
            var title = new StringBuilder($"Frontpercent of {target.DisplayName ?? target.Name} (`{target.Hid}`) in ");
 | 
			
		||||
            if (targetSystem.Name != null) 
 | 
			
		||||
                title.Append($"{targetSystem.Name} (`{targetSystem.Hid}`)");
 | 
			
		||||
            else
 | 
			
		||||
                title.Append($"`{targetSystem.Hid}`");
 | 
			
		||||
 | 
			
		||||
            var frontpercent = await _db.Execute(c => _repo.GetFrontBreakdown(c, targetSystem.Id, target.Id, rangeStart.Value.ToInstant(), now));
 | 
			
		||||
            await ctx.Reply(embed: await _embeds.CreateFrontPercentEmbed(frontpercent, targetSystem, target, targetSystem.Zone, ctx.LookupContextFor(targetSystem), title.ToString()));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private async Task<PKSystem> GetGroupSystem(Context ctx, PKGroup target, IPKConnection conn)
 | 
			
		||||
        {
 | 
			
		||||
            var system = ctx.System;
 | 
			
		||||
 
 | 
			
		||||
@@ -124,8 +124,14 @@ namespace PluralKit.Bot
 | 
			
		||||
            if (rangeStart == null) throw Errors.InvalidDateTime(durationStr);
 | 
			
		||||
            if (rangeStart.Value.ToInstant() > now) throw Errors.FrontPercentTimeInFuture;
 | 
			
		||||
 | 
			
		||||
            var frontpercent = await _db.Execute(c => _repo.GetFrontBreakdown(c, system.Id, rangeStart.Value.ToInstant(), now));
 | 
			
		||||
            await ctx.Reply(embed: await _embeds.CreateFrontPercentEmbed(frontpercent, system.Zone, ctx.LookupContextFor(system)));
 | 
			
		||||
            var title = new StringBuilder($"Frontpercent of ");
 | 
			
		||||
            if (system.Name != null) 
 | 
			
		||||
                title.Append($"{system.Name} (`{system.Hid}`)");
 | 
			
		||||
            else
 | 
			
		||||
                title.Append($"`{system.Hid}`");
 | 
			
		||||
 | 
			
		||||
            var frontpercent = await _db.Execute(c => _repo.GetFrontBreakdown(c, system.Id, null, rangeStart.Value.ToInstant(), now));
 | 
			
		||||
            await ctx.Reply(embed: await _embeds.CreateFrontPercentEmbed(frontpercent, system, null, system.Zone, ctx.LookupContextFor(system), title.ToString()));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -293,12 +293,13 @@ namespace PluralKit.Bot {
 | 
			
		||||
            return eb.Build();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Task<Embed> CreateFrontPercentEmbed(FrontBreakdown breakdown, DateTimeZone tz, LookupContext ctx)
 | 
			
		||||
        public Task<Embed> CreateFrontPercentEmbed(FrontBreakdown breakdown, PKSystem system, PKGroup group, DateTimeZone tz, LookupContext ctx, string embedTitle)
 | 
			
		||||
        {
 | 
			
		||||
            var actualPeriod = breakdown.RangeEnd - breakdown.RangeStart;
 | 
			
		||||
            var eb = new EmbedBuilder()
 | 
			
		||||
                .Title(embedTitle)
 | 
			
		||||
                .Color(DiscordUtils.Gray)
 | 
			
		||||
                .Footer(new($"Since {breakdown.RangeStart.FormatZoned(tz)} ({actualPeriod.FormatDuration()} ago)"));
 | 
			
		||||
                .Footer(new($"System ID: {system.Hid} | {(group != null ? $"Group ID: {group.Hid} | " : "") }Since {breakdown.RangeStart.FormatZoned(tz)} ({actualPeriod.FormatDuration()} ago)"));
 | 
			
		||||
 | 
			
		||||
            var maxEntriesToDisplay = 24; // max 25 fields allowed in embed - reserve 1 for "others"
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -122,7 +122,7 @@ namespace PluralKit.Core
 | 
			
		||||
            await GetSwitches(conn, system).FirstOrDefaultAsync();
 | 
			
		||||
 | 
			
		||||
        public async Task<IEnumerable<SwitchListEntry>> GetPeriodFronters(IPKConnection conn,
 | 
			
		||||
                                                                          SystemId system, Instant periodStart,
 | 
			
		||||
                                                                          SystemId system, GroupId? group, Instant periodStart,
 | 
			
		||||
                                                                          Instant periodEnd)
 | 
			
		||||
        {
 | 
			
		||||
            // TODO: IAsyncEnumerable-ify this one
 | 
			
		||||
@@ -139,7 +139,14 @@ namespace PluralKit.Core
 | 
			
		||||
                new {Switches = switchMembers.Select(m => m.Member.Value).Distinct().ToList()});
 | 
			
		||||
            var memberObjects = membersList.ToDictionary(m => m.Id);
 | 
			
		||||
 | 
			
		||||
            // check if a group ID is provided. if so, query DB for all members of said group, otherwise use membersList
 | 
			
		||||
            var groupMembersList = group != null ? await conn.QueryAsync<PKMember>(
 | 
			
		||||
                "select * from members inner join group_members on members.id = group_members.member_id where group_id = @id",
 | 
			
		||||
                new {id = group}) : membersList;
 | 
			
		||||
            var groupMemberObjects = groupMembersList.ToDictionary(m => m.Id);
 | 
			
		||||
 | 
			
		||||
            // Initialize entries - still need to loop to determine the TimespanEnd below
 | 
			
		||||
            // use groupMemberObjects to make sure no members outside of the specified group (if present) are selected
 | 
			
		||||
            var entries =
 | 
			
		||||
                from item in switchMembers
 | 
			
		||||
                group item by item.Timestamp
 | 
			
		||||
@@ -147,7 +154,7 @@ namespace PluralKit.Core
 | 
			
		||||
                select new SwitchListEntry
 | 
			
		||||
                {
 | 
			
		||||
                    TimespanStart = g.Key,
 | 
			
		||||
                    Members = g.Where(x => x.Member != default(MemberId)).Select(x => memberObjects[x.Member])
 | 
			
		||||
                    Members = g.Where(x => x.Member != default(MemberId) && groupMemberObjects.Any(m => x.Member == m.Key) ).Select(x => memberObjects[x.Member])
 | 
			
		||||
                        .ToList()
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
@@ -174,7 +181,7 @@ namespace PluralKit.Core
 | 
			
		||||
            return outList;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public async Task<FrontBreakdown> GetFrontBreakdown(IPKConnection conn, SystemId system, Instant periodStart,
 | 
			
		||||
        public async Task<FrontBreakdown> GetFrontBreakdown(IPKConnection conn, SystemId system, GroupId? group, Instant periodStart,
 | 
			
		||||
                                                            Instant periodEnd)
 | 
			
		||||
        {
 | 
			
		||||
            // TODO: this doesn't belong in the repo
 | 
			
		||||
@@ -188,7 +195,7 @@ namespace PluralKit.Core
 | 
			
		||||
            var actualStart = periodEnd; // will be "pulled" down
 | 
			
		||||
            var actualEnd = periodStart; // will be "pulled" up
 | 
			
		||||
 | 
			
		||||
            foreach (var sw in await GetPeriodFronters(conn, system, periodStart, periodEnd))
 | 
			
		||||
            foreach (var sw in await GetPeriodFronters(conn, system, group, periodStart, periodEnd))
 | 
			
		||||
            {
 | 
			
		||||
                var span = sw.TimespanEnd - sw.TimespanStart;
 | 
			
		||||
                foreach (var member in sw.Members)
 | 
			
		||||
 
 | 
			
		||||
@@ -51,7 +51,7 @@ namespace PluralKit.Core
 | 
			
		||||
 | 
			
		||||
            // Export switches
 | 
			
		||||
            var switches = new List<DataFileSwitch>();
 | 
			
		||||
            var switchList = await _repo.GetPeriodFronters(conn, system.Id, Instant.FromDateTimeUtc(DateTime.MinValue.ToUniversalTime()), SystemClock.Instance.GetCurrentInstant());
 | 
			
		||||
            var switchList = await _repo.GetPeriodFronters(conn, system.Id, null, Instant.FromDateTimeUtc(DateTime.MinValue.ToUniversalTime()), SystemClock.Instance.GetCurrentInstant());
 | 
			
		||||
            switches.AddRange(switchList.Select(x => new DataFileSwitch
 | 
			
		||||
            {
 | 
			
		||||
                Timestamp = x.TimespanStart.FormatExport(),
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user