Example #1
0
    public async Task Name(Context ctx, PKMember target)
    {
        ctx.CheckSystem().CheckOwnMember(target);

        var newName = ctx.RemainderOrNull() ?? throw new PKSyntaxError("You must pass a new name for the member.");

        // Hard name length cap
        if (newName.Length > Limits.MaxMemberNameLength)
        {
            throw Errors.StringTooLongError("Member name", newName.Length, Limits.MaxMemberNameLength);
        }

        // Warn if there's already a member by this name
        var existingMember = await ctx.Repository.GetMemberByName(ctx.System.Id, newName);

        if (existingMember != null && existingMember.Id != target.Id)
        {
            var msg =
                $"{Emojis.Warn} You already have a member in your system with the name \"{existingMember.NameFor(ctx)}\" (`{existingMember.Hid}`). Do you want to rename this member to that name too?";
            if (!await ctx.PromptYesNo(msg, "Rename"))
            {
                throw new PKError("Member renaming cancelled.");
            }
        }

        // Rename the member
        var patch = new MemberPatch {
            Name = Partial <string> .Present(newName)
        };
        await ctx.Repository.UpdateMember(target.Id, patch);

        await ctx.Reply($"{Emojis.Success} Member renamed.");

        if (newName.Contains(" "))
        {
            await ctx.Reply(
                $"{Emojis.Note} Note that this member's name now contains spaces. You will need to surround it with \"double quotes\" when using commands referring to it.");
        }
        if (target.DisplayName != null)
        {
            await ctx.Reply(
                $"{Emojis.Note} Note that this member has a display name set ({target.DisplayName}), and will be proxied using that name instead.");
        }

        if (ctx.Guild != null)
        {
            var memberGuildConfig = await ctx.Repository.GetMemberGuild(ctx.Guild.Id, target.Id);

            if (memberGuildConfig.DisplayName != null)
            {
                await ctx.Reply(
                    $"{Emojis.Note} Note that this member has a server name set ({memberGuildConfig.DisplayName}) in this server ({ctx.Guild.Name}), and will be proxied using that name here.");
            }
        }
    }
Example #2
0
        public async Task Description(Context ctx, PKMember target)
        {
            if (MatchClear(ctx))
            {
                CheckEditMemberPermission(ctx, target);
                target.Description = null;

                await _data.SaveMember(target);

                await ctx.Reply($"{Emojis.Success} Member description cleared.");
            }
            else if (!ctx.HasNext())
            {
                CheckReadMemberPermission(ctx, target);
                if (target.Description == null)
                {
                    if (ctx.System?.Id == target.System)
                    {
                        await ctx.Reply($"This member does not have a description set. To set one, type `pk;member {target.Hid} description <description>`.");
                    }
                    else
                    {
                        await ctx.Reply("This member does not have a description set.");
                    }
                }
                else if (ctx.MatchFlag("r", "raw"))
                {
                    await ctx.Reply($"```\n{target.Description.SanitizeMentions()}\n```");
                }
                else
                {
                    await ctx.Reply(embed : new DiscordEmbedBuilder()
                                    .WithTitle("Member description")
                                    .WithDescription(target.Description)
                                    .AddField("\u200B", $"To print the description with formatting, type `pk;member {target.Hid} description -raw`."
                                              + (ctx.System?.Id == target.System ? $" To clear it, type `pk;member {target.Hid} description -clear`." : ""))
                                    .Build());
                }
            }
            else
            {
                CheckEditMemberPermission(ctx, target);

                var description = ctx.RemainderOrNull().NormalizeLineEndSpacing();
                if (description.IsLongerThan(Limits.MaxDescriptionLength))
                {
                    throw Errors.DescriptionTooLongError(description.Length);
                }
                target.Description = description;

                await _data.SaveMember(target);

                await ctx.Reply($"{Emojis.Success} Member description changed.");
            }
        }
Example #3
0
        public async Task DisplayName(Context ctx, PKMember target)
        {
            async Task PrintSuccess(string text)
            {
                var successStr = text;

                if (ctx.Guild != null)
                {
                    var memberGuildConfig = await _db.Execute(c => c.QueryOrInsertMemberGuildConfig(ctx.Guild.Id, target.Id));

                    if (memberGuildConfig.DisplayName != null)
                    {
                        successStr += $" However, this member has a server name set in this server ({ctx.Guild.Name}), and will be proxied using that name, \"{memberGuildConfig.DisplayName}\", here.";
                    }
                }

                await ctx.Reply(successStr);
            }

            if (ctx.MatchClear())
            {
                CheckEditMemberPermission(ctx, target);

                var patch = new MemberPatch {
                    DisplayName = Partial <string> .Null()
                };
                await _db.Execute(conn => conn.UpdateMember(target.Id, patch));

                await PrintSuccess($"{Emojis.Success} Member display name cleared. This member will now be proxied using their member name \"{target.NameFor(ctx)}\".");
            }
            else if (!ctx.HasNext())
            {
                // No perms check, display name isn't covered by member privacy
                var eb = await CreateMemberNameInfoEmbed(ctx, target);

                if (ctx.System?.Id == target.System)
                {
                    eb.WithDescription($"To change display name, type `pk;member {target.Hid} displayname <display name>`.\nTo clear it, type `pk;member {target.Hid} displayname -clear`.");
                }
                await ctx.Reply(embed : eb.Build());
            }
            else
            {
                CheckEditMemberPermission(ctx, target);

                var newDisplayName = ctx.RemainderOrNull();

                var patch = new MemberPatch {
                    DisplayName = Partial <string> .Present(newDisplayName)
                };
                await _db.Execute(conn => conn.UpdateMember(target.Id, patch));

                await PrintSuccess($"{Emojis.Success} Member display name changed. This member will now be proxied using the name \"{newDisplayName}\".");
            }
        }
Example #4
0
        public async Task Pronouns(Context ctx, PKMember target)
        {
            if (await ctx.MatchClear("this member's pronouns"))
            {
                ctx.CheckOwnMember(target);

                var patch = new MemberPatch {
                    Pronouns = Partial <string> .Null()
                };
                await _db.Execute(conn => _repo.UpdateMember(conn, target.Id, patch));

                await ctx.Reply($"{Emojis.Success} Member pronouns cleared.");
            }
            else if (!ctx.HasNext())
            {
                if (!target.PronounPrivacy.CanAccess(ctx.LookupContextFor(target.System)))
                {
                    throw Errors.LookupNotAllowed;
                }
                if (target.Pronouns == null)
                {
                    if (ctx.System?.Id == target.System)
                    {
                        await ctx.Reply($"This member does not have pronouns set. To set some, type `pk;member {target.Reference()} pronouns <pronouns>`.");
                    }
                    else
                    {
                        await ctx.Reply("This member does not have pronouns set.");
                    }
                }
                else
                {
                    await ctx.Reply($"**{target.NameFor(ctx)}**'s pronouns are **{target.Pronouns}**."
                                    + (ctx.System?.Id == target.System ? $" To clear them, type `pk;member {target.Reference()} pronouns -clear`." : ""));
                }
            }
            else
            {
                ctx.CheckOwnMember(target);

                var pronouns = ctx.RemainderOrNull().NormalizeLineEndSpacing();
                if (pronouns.IsLongerThan(Limits.MaxPronounsLength))
                {
                    throw Errors.MemberPronounsTooLongError(pronouns.Length);
                }

                var patch = new MemberPatch {
                    Pronouns = Partial <string> .Present(pronouns)
                };
                await _db.Execute(conn => _repo.UpdateMember(conn, target.Id, patch));

                await ctx.Reply($"{Emojis.Success} Member pronouns changed.");
            }
        }
Example #5
0
        public Embed CreateLoggedMessageEmbed(PKSystem system, PKMember member, ulong messageId, ulong originalMsgId, IUser sender, string content, IGuildChannel channel)
        {
            // TODO: pronouns in ?-reacted response using this card
            var timestamp = SnowflakeUtils.FromSnowflake(messageId);

            return(new EmbedBuilder()
                   .WithAuthor($"#{channel.Name}: {member.Name}", member.AvatarUrl)
                   .WithDescription(content)
                   .WithFooter($"System ID: {system.Hid} | Member ID: {member.Hid} | Sender: {sender.Username}#{sender.Discriminator} ({sender.Id}) | Message ID: {messageId} | Original Message ID: {originalMsgId}")
                   .WithTimestamp(timestamp)
                   .Build());
        }
Example #6
0
        private async Task AutoproxyMember(Context ctx, PKMember member)
        {
            ctx.CheckOwnMember(member);

            var settings = await _data.GetSystemGuildSettings(ctx.System, ctx.Guild.Id);

            settings.AutoproxyMode   = AutoproxyMode.Member;
            settings.AutoproxyMember = member.Id;
            await _data.SetSystemGuildSettings(ctx.System, ctx.Guild.Id, settings);

            await ctx.Reply($"{Emojis.Success} Autoproxy set to **{member.Name}** in this server.");
        }
Example #7
0
 private async Task HandleMemberCommandTargeted(Context ctx, PKMember target)
 {
     // Commands that have a member target (eg. pk;member <member> delete)
     if (ctx.Match("rename", "name", "changename", "setname"))
     {
         await ctx.Execute <MemberCommands>(MemberRename, m => m.RenameMember(ctx, target));
     }
     else if (ctx.Match("description", "info", "bio", "text", "desc"))
     {
         await ctx.Execute <MemberCommands>(MemberDesc, m => m.MemberDescription(ctx, target));
     }
     else if (ctx.Match("pronouns", "pronoun"))
     {
         await ctx.Execute <MemberCommands>(MemberPronouns, m => m.MemberPronouns(ctx, target));
     }
     else if (ctx.Match("color", "colour"))
     {
         await ctx.Execute <MemberCommands>(MemberColor, m => m.MemberColor(ctx, target));
     }
     else if (ctx.Match("birthday", "bday", "birthdate", "cakeday", "bdate"))
     {
         await ctx.Execute <MemberCommands>(MemberBirthday, m => m.MemberBirthday(ctx, target));
     }
     else if (ctx.Match("proxy", "tags", "proxytags", "brackets"))
     {
         await ctx.Execute <MemberCommands>(MemberProxy, m => m.MemberProxy(ctx, target));
     }
     else if (ctx.Match("delete", "remove", "destroy", "erase", "yeet"))
     {
         await ctx.Execute <MemberCommands>(MemberDelete, m => m.MemberDelete(ctx, target));
     }
     else if (ctx.Match("avatar", "profile", "picture", "icon", "image", "pfp", "pic"))
     {
         await ctx.Execute <MemberCommands>(MemberAvatar, m => m.MemberAvatar(ctx, target));
     }
     else if (ctx.Match("displayname", "dn", "dname", "nick", "nickname"))
     {
         await ctx.Execute <MemberCommands>(MemberDisplayName, m => m.MemberDisplayName(ctx, target));
     }
     else if (ctx.Match("keepproxy", "keeptags", "showtags"))
     {
         await ctx.Execute <MemberCommands>(MemberKeepProxy, m => m.MemberKeepProxy(ctx, target));
     }
     else if (!ctx.HasNext()) // Bare command
     {
         await ctx.Execute <MemberCommands>(MemberInfo, m => m.ViewMember(ctx, target));
     }
     else
     {
         await PrintCommandNotFoundError(ctx, MemberInfo, MemberRename, MemberDisplayName, MemberDesc, MemberPronouns, MemberColor, MemberBirthday, MemberProxy, MemberDelete, MemberAvatar, SystemList);
     }
 }
Example #8
0
        public async Task KeepProxy(Context ctx, PKMember target)
        {
            if (ctx.System == null)
            {
                throw Errors.NoSystemError;
            }
            if (target.System != ctx.System.Id)
            {
                throw Errors.NotOwnMemberError;
            }

            bool newValue;

            if (ctx.Match("on", "enabled", "true", "yes"))
            {
                newValue = true;
            }
            else if (ctx.Match("off", "disabled", "false", "no"))
            {
                newValue = false;
            }
            else if (ctx.HasNext())
            {
                throw new PKSyntaxError("You must pass either \"on\" or \"off\".");
            }
            else
            {
                if (target.KeepProxy)
                {
                    await ctx.Reply("This member has keepproxy **enabled**, which means proxy tags will be **included** in the resulting message when proxying.");
                }
                else
                {
                    await ctx.Reply("This member has keepproxy **disabled**, which means proxy tags will **not** be included in the resulting message when proxying.");
                }
                return;
            };

            var patch = new MemberPatch {
                KeepProxy = Partial <bool> .Present(newValue)
            };
            await _db.Execute(conn => _repo.UpdateMember(conn, target.Id, patch));

            if (newValue)
            {
                await ctx.Reply($"{Emojis.Success} Member proxy tags will now be included in the resulting message when proxying.");
            }
            else
            {
                await ctx.Reply($"{Emojis.Success} Member proxy tags will now not be included in the resulting message when proxying.");
            }
        }
Example #9
0
        public async Task MemberAutoproxy(Context ctx, PKMember target)
        {
            if (ctx.System == null)
            {
                throw Errors.NoSystemError;
            }
            if (target.System != ctx.System.Id)
            {
                throw Errors.NotOwnMemberError;
            }

            bool newValue;

            if (ctx.Match("on", "enabled", "true", "yes") || ctx.MatchFlag("on", "enabled", "true", "yes"))
            {
                newValue = true;
            }
            else if (ctx.Match("off", "disabled", "false", "no") || ctx.MatchFlag("off", "disabled", "false", "no"))
            {
                newValue = false;
            }
            else if (ctx.HasNext())
            {
                throw new PKSyntaxError("You must pass either \"on\" or \"off\".");
            }
            else
            {
                if (target.AllowAutoproxy)
                {
                    await ctx.Reply("Latch/front autoproxy are **enabled** for this member. This member will be automatically proxied when autoproxy is set to latch or front mode.");
                }
                else
                {
                    await ctx.Reply("Latch/front autoproxy are **disabled** for this member. This member will not be automatically proxied when autoproxy is set to latch or front mode.");
                }
                return;
            };

            var patch = new MemberPatch {
                AllowAutoproxy = Partial <bool> .Present(newValue)
            };
            await _db.Execute(conn => _repo.UpdateMember(conn, target.Id, patch));

            if (newValue)
            {
                await ctx.Reply($"{Emojis.Success} Latch / front autoproxy have been **enabled** for this member.");
            }
            else
            {
                await ctx.Reply($"{Emojis.Success} Latch / front autoproxy have been **disabled** for this member.");
            }
        }
Example #10
0
        public DiscordEmbed CreateLoggedMessageEmbed(PKSystem system, PKMember member, ulong messageId, ulong originalMsgId, DiscordUser sender, string content, DiscordChannel channel)
        {
            // TODO: pronouns in ?-reacted response using this card
            var timestamp = DiscordUtils.SnowflakeToInstant(messageId);

            return(new DiscordEmbedBuilder()
                   .WithAuthor($"#{channel.Name}: {member.Name}", iconUrl: DiscordUtils.WorkaroundForUrlBug(member.AvatarUrl))
                   .WithThumbnailUrl(member.AvatarUrl)
                   .WithDescription(content?.NormalizeLineEndSpacing())
                   .WithFooter($"System ID: {system.Hid} | Member ID: {member.Hid} | Sender: {sender.Username}#{sender.Discriminator} ({sender.Id}) | Message ID: {messageId} | Original Message ID: {originalMsgId}")
                   .WithTimestamp(timestamp.ToDateTimeOffset())
                   .Build());
        }
Example #11
0
        public async Task <ActionResult <PKMember> > PostMember([FromBody] PKMember newMember)
        {
            var system = _auth.CurrentSystem;

            if (newMember.Name == null)
            {
                return(BadRequest("Member name cannot be null."));
            }

            // Explicit bounds checks
            if (newMember.Name != null && newMember.Name.Length > Limits.MaxMemberNameLength)
            {
                return(BadRequest($"Member name too long ({newMember.Name.Length} > {Limits.MaxMemberNameLength}."));
            }
            if (newMember.Pronouns != null && newMember.Pronouns.Length > Limits.MaxPronounsLength)
            {
                return(BadRequest($"Member pronouns too long ({newMember.Pronouns.Length} > {Limits.MaxPronounsLength}."));
            }
            if (newMember.Description != null && newMember.Description.Length > Limits.MaxDescriptionLength)
            {
                return(BadRequest($"Member descriptions too long ({newMember.Description.Length} > {Limits.MaxDescriptionLength}."));
            }

            // Sanity bounds checks
            if (newMember.AvatarUrl != null && newMember.AvatarUrl.Length > 1000)
            {
                return(BadRequest());
            }
            if (newMember.Prefix != null && newMember.Prefix.Length > 1000)
            {
                return(BadRequest());
            }
            if (newMember.Suffix != null && newMember.Suffix.Length > 1000)
            {
                return(BadRequest());
            }

            var member = await _members.Create(system, newMember.Name);

            member.Name        = newMember.Name;
            member.Color       = newMember.Color;
            member.AvatarUrl   = newMember.AvatarUrl;
            member.Birthday    = newMember.Birthday;
            member.Pronouns    = newMember.Pronouns;
            member.Description = newMember.Description;
            member.Prefix      = newMember.Prefix;
            member.Suffix      = newMember.Suffix;
            await _members.Save(member);

            return(Ok(member));
        }
        public static JObject ToJson(this PKMember member, LookupContext ctx)
        {
            var includePrivacy = ctx == LookupContext.ByOwner;

            var o = new JObject();

            o.Add("id", member.Hid);
            o.Add("name", member.NameFor(ctx));
            // o.Add("color", member.ColorPrivacy.CanAccess(ctx) ? member.Color : null);
            o.Add("color", member.Color);
            o.Add("display_name", member.NamePrivacy.CanAccess(ctx) ? member.DisplayName : null);
            o.Add("birthday", member.BirthdayFor(ctx)?.FormatExport());
            o.Add("pronouns", member.PronounsFor(ctx));
            o.Add("avatar_url", member.AvatarFor(ctx));
            o.Add("description", member.DescriptionFor(ctx));

            var tagArray = new JArray();

            foreach (var tag in member.ProxyTags)
            {
                tagArray.Add(new JObject {
                    { "prefix", tag.Prefix }, { "suffix", tag.Suffix }
                });
            }
            o.Add("proxy_tags", tagArray);

            o.Add("keep_proxy", member.KeepProxy);

            o.Add("privacy", includePrivacy ? (member.MemberVisibility.LevelName()) : null);

            o.Add("visibility", includePrivacy ? (member.MemberVisibility.LevelName()) : null);
            o.Add("name_privacy", includePrivacy ? (member.NamePrivacy.LevelName()) : null);
            o.Add("description_privacy", includePrivacy ? (member.DescriptionPrivacy.LevelName()) : null);
            o.Add("birthday_privacy", includePrivacy ? (member.BirthdayPrivacy.LevelName()) : null);
            o.Add("pronoun_privacy", includePrivacy ? (member.PronounPrivacy.LevelName()) : null);
            o.Add("avatar_privacy", includePrivacy ? (member.AvatarPrivacy.LevelName()) : null);
            // o.Add("color_privacy", ctx == LookupContext.ByOwner ? (member.ColorPrivacy.LevelName()) : null);
            o.Add("metadata_privacy", includePrivacy ? (member.MetadataPrivacy.LevelName()) : null);

            o.Add("created", member.CreatedFor(ctx)?.FormatExport());

            if (member.ProxyTags.Count > 0)
            {
                // Legacy compatibility only, TODO: remove at some point
                o.Add("prefix", member.ProxyTags?.FirstOrDefault().Prefix);
                o.Add("suffix", member.ProxyTags?.FirstOrDefault().Suffix);
            }

            return(o);
        }
Example #13
0
 private async Task AvatarCommandTree(AvatarLocation location, Context ctx, PKMember target, MemberGuildSettings?guildData)
 {
     if (ctx.Match("clear", "remove", "reset") || ctx.MatchFlag("c", "clear"))
     {
         await AvatarClear(location, ctx, target, guildData);
     }
     else if (ctx.RemainderOrNull() == null && ctx.Message.Attachments.Count == 0)
     {
         await AvatarShow(location, ctx, target, guildData);
     }
     else if (await ctx.MatchUser() is {} user)
     {
         await AvatarFromUser(location, ctx, target, user);
     }
Example #14
0
        public async Task Pronouns(Context ctx, PKMember target)
        {
            if (MatchClear(ctx))
            {
                CheckEditMemberPermission(ctx, target);
                target.Pronouns = null;

                await _data.SaveMember(target);

                await ctx.Reply($"{Emojis.Success} Member pronouns cleared.");
            }
            else if (!ctx.HasNext())
            {
                if (!target.PronounPrivacy.CanAccess(ctx.LookupContextFor(target.System)))
                {
                    throw Errors.LookupNotAllowed;
                }
                if (target.Pronouns == null)
                {
                    if (ctx.System?.Id == target.System)
                    {
                        await ctx.Reply($"This member does not have pronouns set. To set some, type `pk;member {target.Hid} pronouns <pronouns>`.");
                    }
                    else
                    {
                        await ctx.Reply("This member does not have pronouns set.");
                    }
                }
                else
                {
                    await ctx.Reply($"**{target.NameFor(ctx)}**'s pronouns are **{target.Pronouns}**."
                                    + (ctx.System?.Id == target.System ? $" To clear them, type `pk;member {target.Hid} pronouns -clear`." : ""));
                }
            }
            else
            {
                CheckEditMemberPermission(ctx, target);

                var pronouns = ctx.RemainderOrNull().NormalizeLineEndSpacing();
                if (pronouns.IsLongerThan(Limits.MaxPronounsLength))
                {
                    throw Errors.MemberPronounsTooLongError(pronouns.Length);
                }
                target.Pronouns = pronouns;

                await _data.SaveMember(target);

                await ctx.Reply($"{Emojis.Success} Member pronouns changed.");
            }
        }
Example #15
0
 private async Task <DataFileMember> ExportMember(PKMember member) => new DataFileMember
 {
     Id                                             = member.Hid,
     Name                                           = member.Name,
     Description                                    = member.Description,
     Birthday                                       = member.Birthday != null?Formats.DateExportFormat.Format(member.Birthday.Value) : null,
                                       Pronouns     = member.Pronouns,
                                       Color        = member.Color,
                                       AvatarUrl    = member.AvatarUrl,
                                       Prefix       = member.Prefix,
                                       Suffix       = member.Suffix,
                                       Created      = Formats.TimestampExportFormat.Format(member.Created),
                                       MessageCount = await _members.MessageCount(member)
 };
Example #16
0
        public async Task RenameMember(Context ctx, PKMember target)
        {
            // TODO: this method is pretty much a 1:1 copy/paste of the above creation method, find a way to clean?
            if (ctx.System == null)
            {
                throw Errors.NoSystemError;
            }
            if (target.System != ctx.System.Id)
            {
                throw Errors.NotOwnMemberError;
            }

            var newName = ctx.RemainderOrNull();

            // Hard name length cap
            if (newName.Length > Limits.MaxMemberNameLength)
            {
                throw Errors.MemberNameTooLongError(newName.Length);
            }

            // Warn if there's already a member by this name
            var existingMember = await _data.GetMemberByName(ctx.System, newName);

            if (existingMember != null)
            {
                var msg = await ctx.Reply($"{Emojis.Warn} You already have a member in your system with the name \"{existingMember.Name.SanitizeMentions()}\" (`{existingMember.Hid}`). Do you want to rename this member to that name too?");

                if (!await ctx.PromptYesNo(msg))
                {
                    throw new PKError("Member renaming cancelled.");
                }
            }

            // Rename the member
            target.Name = newName;
            await _data.SaveMember(target);

            await ctx.Reply($"{Emojis.Success} Member renamed.");

            if (newName.Contains(" "))
            {
                await ctx.Reply($"{Emojis.Note} Note that this member's name now contains spaces. You will need to surround it with \"double quotes\" when using commands referring to it.");
            }
            if (target.DisplayName != null)
            {
                await ctx.Reply($"{Emojis.Note} Note that this member has a display name set ({target.DisplayName.SanitizeMentions()}), and will be proxied using that name instead.");
            }

            await _proxyCache.InvalidateResultsForSystem(ctx.System);
        }
Example #17
0
        private async Task <EmbedBuilder> CreateMemberNameInfoEmbed(Context ctx, PKMember target)
        {
            var lcx = ctx.LookupContextFor(target);

            MemberGuildSettings memberGuildConfig = null;

            if (ctx.Guild != null)
            {
                memberGuildConfig = await _db.Execute(c => _repo.GetMemberGuild(c, ctx.Guild.Id, target.Id));
            }

            var eb = new EmbedBuilder()
                     .Title($"Member names")
                     .Footer(new($"Member ID: {target.Hid} | Active name in bold. Server name overrides display name, which overrides base name."));

            if (target.DisplayName == null && memberGuildConfig?.DisplayName == null)
            {
                eb.Field(new("Name", $"**{target.NameFor(ctx)}**"));
            }
            else
            {
                eb.Field(new("Name", target.NameFor(ctx)));
            }

            if (target.NamePrivacy.CanAccess(lcx))
            {
                if (target.DisplayName != null && memberGuildConfig?.DisplayName == null)
                {
                    eb.Field(new("Display Name", $"**{target.DisplayName}**"));
                }
                else
                {
                    eb.Field(new("Display Name", target.DisplayName ?? "*(none)*"));
                }
            }

            if (ctx.Guild != null)
            {
                if (memberGuildConfig?.DisplayName != null)
                {
                    eb.Field(new($"Server Name (in {ctx.Guild.Name})", $"**{memberGuildConfig.DisplayName}**"));
                }
                else
                {
                    eb.Field(new($"Server Name (in {ctx.Guild.Name})", memberGuildConfig?.DisplayName ?? "*(none)*"));
                }
            }

            return(eb);
        }
Example #18
0
        public Embed CreateEditedMessageEmbed(PKSystem system, PKMember member, ulong messageId, ulong originalMsgId, User sender, string content, string oldContent, Channel channel)
        {
            var timestamp = DiscordUtils.SnowflakeToInstant(messageId);
            var name      = member.NameFor(LookupContext.ByNonOwner);

            return(new EmbedBuilder()
                   .Author(new($"[Edited] #{channel.Name}: {name}", IconUrl: DiscordUtils.WorkaroundForUrlBug(member.AvatarFor(LookupContext.ByNonOwner))))
                   .Thumbnail(new(member.AvatarFor(LookupContext.ByNonOwner)))
                   .Field(new("Old message", oldContent?.NormalizeLineEndSpacing().Truncate(1000)))
                   .Description(content?.NormalizeLineEndSpacing())
                   .Footer(new($"System ID: {system.Hid} | Member ID: {member.Hid} | Sender: {sender.Username}#{sender.Discriminator} ({sender.Id}) | Message ID: {messageId} | Original Message ID: {originalMsgId}"))
                   .Timestamp(timestamp.ToDateTimeOffset().ToString("O"))
                   .Build());
        }
Example #19
0
        public async Task Birthday(Context ctx, PKMember target)
        {
            if (await ctx.MatchClear("this member's birthday"))
            {
                ctx.CheckOwnMember(target);

                var patch = new MemberPatch {
                    Birthday = Partial <LocalDate?> .Null()
                };
                await _db.Execute(conn => _repo.UpdateMember(conn, target.Id, patch));

                await ctx.Reply($"{Emojis.Success} Member birthdate cleared.");
            }
            else if (!ctx.HasNext())
            {
                if (!target.BirthdayPrivacy.CanAccess(ctx.LookupContextFor(target.System)))
                {
                    throw Errors.LookupNotAllowed;
                }

                if (target.Birthday == null)
                {
                    await ctx.Reply("This member does not have a birthdate set."
                                    + (ctx.System?.Id == target.System ? $" To set one, type `pk;member {target.Reference()} birthdate <birthdate>`." : ""));
                }
                else
                {
                    await ctx.Reply($"This member's birthdate is **{target.BirthdayString}**."
                                    + (ctx.System?.Id == target.System ? $" To clear it, type `pk;member {target.Reference()} birthdate -clear`." : ""));
                }
            }
            else
            {
                ctx.CheckOwnMember(target);

                var birthdayStr = ctx.RemainderOrNull();
                var birthday    = DateUtils.ParseDate(birthdayStr, true);
                if (birthday == null)
                {
                    throw Errors.BirthdayParseError(birthdayStr);
                }

                var patch = new MemberPatch {
                    Birthday = Partial <LocalDate?> .Present(birthday)
                };
                await _db.Execute(conn => _repo.UpdateMember(conn, target.Id, patch));

                await ctx.Reply($"{Emojis.Success} Member birthdate changed.");
            }
        }
        public async Task LogMessage(PKSystem system, PKMember member, ulong messageId, ulong originalMsgId, IGuildChannel originalChannel, IUser sender, string content)
        {
            var logChannel = await GetLogChannel(originalChannel.Guild);

            if (logChannel == null)
            {
                return;
            }

            var embed = _embed.CreateLoggedMessageEmbed(system, member, messageId, originalMsgId, sender, content, originalChannel);

            var url = $"https://discordapp.com/channels/{originalChannel.GuildId}/{originalChannel.Id}/{messageId}";
            await logChannel.SendMessageAsync(text : url, embed : embed);
        }
Example #21
0
        public async Task DisplayName(Context ctx, PKMember target)
        {
            async Task PrintSuccess(string text)
            {
                var successStr = text;

                if (ctx.Guild != null)
                {
                    var memberGuildConfig = await _data.GetMemberGuildSettings(target, ctx.Guild.Id);

                    if (memberGuildConfig.DisplayName != null)
                    {
                        successStr += $" However, this member has a server name set in this server ({ctx.Guild.Name.SanitizeMentions()}), and will be proxied using that name, \"{memberGuildConfig.DisplayName.SanitizeMentions()}\", here.";
                    }
                }

                await ctx.Reply(successStr);
            }

            if (MatchClear(ctx))
            {
                CheckEditMemberPermission(ctx, target);

                target.DisplayName = null;
                await _data.SaveMember(target);
                await PrintSuccess($"{Emojis.Success} Member display name cleared. This member will now be proxied using their member name \"{target.Name.SanitizeMentions()}\".");
            }
            else if (!ctx.HasNext())
            {
                // No perms check, display name isn't covered by member privacy
                var eb = await CreateMemberNameInfoEmbed(ctx, target);

                if (ctx.System?.Id == target.System)
                {
                    eb.WithDescription($"To change display name, type `pk;member {target.Hid} displayname <display name>`.\nTo clear it, type `pk;member {target.Hid} displayname -clear`.");
                }
                await ctx.Reply(embed : eb.Build());
            }
            else
            {
                CheckEditMemberPermission(ctx, target);

                var newDisplayName = ctx.RemainderOrNull();
                target.DisplayName = newDisplayName;
                await _data.SaveMember(target);

                await PrintSuccess($"{Emojis.Success} Member display name changed. This member will now be proxied using the name \"{newDisplayName.SanitizeMentions()}\".");
            }
        }
Example #22
0
        public async Task Delete(Context ctx, PKMember target)
        {
            ctx.CheckSystem().CheckOwnMember(target);

            await ctx.Reply($"{Emojis.Warn} Are you sure you want to delete \"{target.NameFor(ctx)}\"? If so, reply to this message with the member's ID (`{target.Hid}`). __***This cannot be undone!***__");

            if (!await ctx.ConfirmWithReply(target.Hid))
            {
                throw Errors.MemberDeleteCancelled;
            }

            await _db.Execute(conn => _repo.DeleteMember(conn, target.Id));

            await ctx.Reply($"{Emojis.Success} Member deleted.");
        }
Example #23
0
        public async Task ServerName(Context ctx, PKMember target)
        {
            ctx.CheckGuildContext();

            if (await ctx.MatchClear("this member's server name"))
            {
                ctx.CheckOwnMember(target);

                var patch = new MemberGuildPatch {
                    DisplayName = null
                };
                await _db.Execute(conn => _repo.UpsertMemberGuild(conn, target.Id, ctx.Guild.Id, patch));

                if (target.DisplayName != null)
                {
                    await ctx.Reply($"{Emojis.Success} Member server name cleared. This member will now be proxied using their global display name \"{target.DisplayName}\" in this server ({ctx.Guild.Name}).");
                }
                else
                {
                    await ctx.Reply($"{Emojis.Success} Member server name cleared. This member will now be proxied using their member name \"{target.NameFor(ctx)}\" in this server ({ctx.Guild.Name}).");
                }
            }
            else if (!ctx.HasNext())
            {
                // No perms check, server name isn't covered by member privacy
                var eb = await CreateMemberNameInfoEmbed(ctx, target);

                if (ctx.System?.Id == target.System)
                {
                    eb.Description($"To change server name, type `pk;member {target.Reference()} servername <server name>`.\nTo clear it, type `pk;member {target.Reference()} servername -clear`.");
                }
                await ctx.Reply(embed : eb.Build());
            }
            else
            {
                ctx.CheckOwnMember(target);

                var newServerName = ctx.RemainderOrNull();

                var patch = new MemberGuildPatch {
                    DisplayName = newServerName
                };
                await _db.Execute(conn => _repo.UpsertMemberGuild(conn, target.Id, ctx.Guild.Id, patch));

                await ctx.Reply($"{Emojis.Success} Member server name changed. This member will now be proxied using the name \"{newServerName}\" in this server ({ctx.Guild.Name}).");
            }
        }
Example #24
0
        public async Task ServerName(Context ctx, PKMember target)
        {
            ctx.CheckGuildContext();

            if (MatchClear(ctx))
            {
                CheckEditMemberPermission(ctx, target);

                await _db.Execute(c =>
                                  c.ExecuteAsync("update member_guild set display_name = null where member = @member and guild = @guild",
                                                 new { member = target.Id, guild = ctx.Guild.Id }));

                if (target.DisplayName != null)
                {
                    await ctx.Reply($"{Emojis.Success} Member server name cleared. This member will now be proxied using their global display name \"{target.DisplayName.SanitizeMentions()}\" in this server ({ctx.Guild.Name.SanitizeMentions()}).");
                }
                else
                {
                    await ctx.Reply($"{Emojis.Success} Member server name cleared. This member will now be proxied using their member name \"{target.Name.SanitizeMentions()}\" in this server ({ctx.Guild.Name.SanitizeMentions()}).");
                }
            }
            else if (!ctx.HasNext())
            {
                // No perms check, server name isn't covered by member privacy
                var eb = await CreateMemberNameInfoEmbed(ctx, target);

                if (ctx.System?.Id == target.System)
                {
                    eb.WithDescription($"To change server name, type `pk;member {target.Hid} servername <server name>`.\nTo clear it, type `pk;member {target.Hid} servername -clear`.");
                }
                await ctx.Reply(embed : eb.Build());
            }
            else
            {
                CheckEditMemberPermission(ctx, target);

                var newServerName = ctx.RemainderOrNull();

                await _db.Execute(c =>
                                  c.ExecuteAsync("update member_guild set display_name = @newServerName where member = @member and guild = @guild",
                                                 new { member = target.Id, guild = ctx.Guild.Id, newServerName }));

                await ctx.Reply($"{Emojis.Success} Member server name changed. This member will now be proxied using the name \"{newServerName.SanitizeMentions()}\" in this server ({ctx.Guild.Name.SanitizeMentions()}).");
            }
        }
Example #25
0
    private Task UpdateAvatar(AvatarLocation location, Context ctx, PKMember target, string?url)
    {
        switch (location)
        {
        case AvatarLocation.Server:
            return(ctx.Repository.UpdateMemberGuild(target.Id, ctx.Guild.Id, new MemberGuildPatch {
                AvatarUrl = url
            }));

        case AvatarLocation.Member:
            return(ctx.Repository.UpdateMember(target.Id, new MemberPatch {
                AvatarUrl = url
            }));

        default:
            throw new ArgumentOutOfRangeException($"Unknown avatar location {location}");
        }
    }
Example #26
0
        private async Task AvatarFromArg(AvatarLocation location, Context ctx, PKMember target, string url)
        {
            ctx.CheckSystem().CheckOwnMember(target);
            if (url.Length > Limits.MaxUriLength)
            {
                throw Errors.InvalidUrl(url);
            }
            await AvatarUtils.VerifyAvatarOrThrow(url);

            await UpdateAvatar(location, ctx, target, url);

            var embed = new DiscordEmbedBuilder().WithImageUrl(url).Build();

            if (location == AvatarLocation.Server)
            {
                await ctx.Reply($"{Emojis.Success} Member server avatar changed. This avatar will now be used when proxying in this server (**{ctx.Guild.Name}**).", embed : embed);
            }
        }
Example #27
0
        public async Task Birthday(Context ctx, PKMember target)
        {
            if (MatchClear(ctx))
            {
                CheckEditMemberPermission(ctx, target);
                target.Birthday = null;
                await _data.SaveMember(target);

                await ctx.Reply($"{Emojis.Success} Member birthdate cleared.");
            }
            else if (!ctx.HasNext())
            {
                if (!target.BirthdayPrivacy.CanAccess(ctx.LookupContextFor(target.System)))
                {
                    throw Errors.LookupNotAllowed;
                }

                if (target.Birthday == null)
                {
                    await ctx.Reply("This member does not have a birthdate set."
                                    + (ctx.System?.Id == target.System ? $" To set one, type `pk;member {target.Hid} birthdate <birthdate>`." : ""));
                }
                else
                {
                    await ctx.Reply($"This member's birthdate is **{target.BirthdayString}**."
                                    + (ctx.System?.Id == target.System ? $" To clear it, type `pk;member {target.Hid} birthdate -clear`." : ""));
                }
            }
            else
            {
                CheckEditMemberPermission(ctx, target);

                var birthdayStr = ctx.RemainderOrNull();
                var birthday    = DateUtils.ParseDate(birthdayStr, true);
                if (birthday == null)
                {
                    throw Errors.BirthdayParseError(birthdayStr);
                }
                target.Birthday = birthday;
                await _data.SaveMember(target);

                await ctx.Reply($"{Emojis.Success} Member birthdate changed.");
            }
        }
Example #28
0
        private async Task <EmbedBuilder> CreateMemberNameInfoEmbed(Context ctx, PKMember target)
        {
            MemberGuildSettings memberGuildConfig = null;

            if (ctx.Guild != null)
            {
                memberGuildConfig = await _data.GetMemberGuildSettings(target, ctx.Guild.Id);
            }

            var eb = new EmbedBuilder().WithTitle($"Member names")
                     .WithFooter($"Member ID: {target.Hid} | Active name in bold. Server name overrides display name, which overrides base name.");

            if (target.DisplayName == null && memberGuildConfig?.DisplayName == null)
            {
                eb.AddField($"Name", $"**{target.Name}**");
            }
            else
            {
                eb.AddField("Name", target.Name);
            }

            if (target.DisplayName != null && memberGuildConfig?.DisplayName == null)
            {
                eb.AddField($"Display Name", $"**{target.DisplayName}**");
            }
            else
            {
                eb.AddField("Display Name", target.DisplayName ?? "*(none)*");
            }

            if (ctx.Guild != null)
            {
                if (memberGuildConfig?.DisplayName != null)
                {
                    eb.AddField($"Server Name (in {ctx.Guild.Name.SanitizeMentions()})", $"**{memberGuildConfig.DisplayName}**");
                }
                else
                {
                    eb.AddField($"Server Name (in {ctx.Guild.Name.SanitizeMentions()})", memberGuildConfig?.DisplayName ?? "*(none)*");
                }
            }

            return(eb);
        }
Example #29
0
    private async Task AvatarShow(AvatarLocation location, Context ctx, PKMember target,
                                  MemberGuildSettings?guildData)
    {
        // todo: this privacy code is really confusing
        // for now, we skip privacy flag/config parsing for this, but it would be good to fix that at some point

        var currentValue = location == AvatarLocation.Member ? target.AvatarUrl : guildData?.AvatarUrl;
        var canAccess    = location != AvatarLocation.Member ||
                           target.AvatarPrivacy.CanAccess(ctx.DirectLookupContextFor(target.System));

        if (string.IsNullOrEmpty(currentValue) || !canAccess)
        {
            if (location == AvatarLocation.Member)
            {
                if (target.System == ctx.System?.Id)
                {
                    throw new PKSyntaxError(
                              "This member does not have an avatar set. Set one by attaching an image to this command, or by passing an image URL or @mention.");
                }
                throw new PKError("This member does not have an avatar set.");
            }

            if (location == AvatarLocation.Server)
            {
                throw new PKError(
                          $"This member does not have a server avatar set. Type `pk;member {target.Reference(ctx)} avatar` to see their global avatar.");
            }
        }

        var field = location == AvatarLocation.Server ? $"server avatar (for {ctx.Guild.Name})" : "avatar";
        var cmd   = location == AvatarLocation.Server ? "serveravatar" : "avatar";

        var eb = new EmbedBuilder()
                 .Title($"{target.NameFor(ctx)}'s {field}")
                 .Image(new Embed.EmbedImage(currentValue?.TryGetCleanCdnUrl()));

        if (target.System == ctx.System?.Id)
        {
            eb.Description($"To clear, use `pk;member {target.Reference(ctx)} {cmd} clear`.");
        }
        await ctx.Reply(embed : eb.Build());
    }
Example #30
0
        private Task UpdateAvatar(AvatarLocation location, Context ctx, PKMember target, string?url)
        {
            switch (location)
            {
            case AvatarLocation.Server:
                var serverPatch = new MemberGuildPatch {
                    AvatarUrl = url
                };
                return(_db.Execute(c => c.UpsertMemberGuild(target.Id, ctx.Guild.Id, serverPatch)));

            case AvatarLocation.Member:
                var memberPatch = new MemberPatch {
                    AvatarUrl = url
                };
                return(_db.Execute(c => c.UpdateMember(target.Id, memberPatch)));

            default:
                throw new ArgumentOutOfRangeException($"Unknown avatar location {location}");
            }
        }