mirror of
https://github.com/PluralKit/PluralKit.git
synced 2026-02-14 09:40:10 +00:00
feat: initial 6-character HID rework
This commit is contained in:
parent
73f43b8cb3
commit
9f56697241
30 changed files with 208 additions and 91 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -9,6 +9,7 @@ target/
|
||||||
.idea/
|
.idea/
|
||||||
.run/
|
.run/
|
||||||
.vscode/
|
.vscode/
|
||||||
|
.mono/
|
||||||
tags/
|
tags/
|
||||||
.DS_Store
|
.DS_Store
|
||||||
mono_crash*
|
mono_crash*
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ namespace PluralKit.API;
|
||||||
public class PKControllerBase: ControllerBase
|
public class PKControllerBase: ControllerBase
|
||||||
{
|
{
|
||||||
private readonly Guid _requestId = Guid.NewGuid();
|
private readonly Guid _requestId = Guid.NewGuid();
|
||||||
private readonly Regex _shortIdRegex = new("^[a-z]{5}$");
|
|
||||||
private readonly Regex _snowflakeRegex = new("^[0-9]{17,19}$");
|
private readonly Regex _snowflakeRegex = new("^[0-9]{17,19}$");
|
||||||
|
|
||||||
private List<PKMember>? _memberLookupCache { get; set; }
|
private List<PKMember>? _memberLookupCache { get; set; }
|
||||||
|
|
@ -46,8 +45,8 @@ public class PKControllerBase: ControllerBase
|
||||||
if (_snowflakeRegex.IsMatch(systemRef))
|
if (_snowflakeRegex.IsMatch(systemRef))
|
||||||
return _repo.GetSystemByAccount(ulong.Parse(systemRef));
|
return _repo.GetSystemByAccount(ulong.Parse(systemRef));
|
||||||
|
|
||||||
if (_shortIdRegex.IsMatch(systemRef))
|
if (systemRef.TryParseHid(out var hid))
|
||||||
return _repo.GetSystemByHid(systemRef);
|
return _repo.GetSystemByHid(hid);
|
||||||
|
|
||||||
return Task.FromResult<PKSystem?>(null);
|
return Task.FromResult<PKSystem?>(null);
|
||||||
}
|
}
|
||||||
|
|
@ -71,8 +70,8 @@ public class PKControllerBase: ControllerBase
|
||||||
if (Guid.TryParse(memberRef, out var guid))
|
if (Guid.TryParse(memberRef, out var guid))
|
||||||
return await _repo.GetMemberByGuid(guid);
|
return await _repo.GetMemberByGuid(guid);
|
||||||
|
|
||||||
if (_shortIdRegex.IsMatch(memberRef))
|
if (memberRef.TryParseHid(out var hid))
|
||||||
return await _repo.GetMemberByHid(memberRef);
|
return await _repo.GetMemberByHid(hid);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -96,8 +95,8 @@ public class PKControllerBase: ControllerBase
|
||||||
if (Guid.TryParse(groupRef, out var guid))
|
if (Guid.TryParse(groupRef, out var guid))
|
||||||
return await _repo.GetGroupByGuid(guid);
|
return await _repo.GetGroupByGuid(guid);
|
||||||
|
|
||||||
if (_shortIdRegex.IsMatch(groupRef))
|
if (groupRef.TryParseHid(out var hid))
|
||||||
return await _repo.GetGroupByHid(groupRef);
|
return await _repo.GetGroupByHid(hid);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,11 +48,12 @@ public class ApplicationCommandProxiedMessage
|
||||||
msg.System,
|
msg.System,
|
||||||
msg.Member,
|
msg.Member,
|
||||||
guild,
|
guild,
|
||||||
|
ctx.Config,
|
||||||
LookupContext.ByNonOwner,
|
LookupContext.ByNonOwner,
|
||||||
DateTimeZone.Utc
|
DateTimeZone.Utc
|
||||||
));
|
));
|
||||||
|
|
||||||
embeds.Add(await _embeds.CreateMessageInfoEmbed(msg, showContent));
|
embeds.Add(await _embeds.CreateMessageInfoEmbed(msg, showContent, ctx.Config));
|
||||||
|
|
||||||
await ctx.Reply(embeds: embeds.ToArray());
|
await ctx.Reply(embeds: embeds.ToArray());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -538,6 +538,8 @@ public partial class CommandTree
|
||||||
return ctx.Execute<Config>(null, m => m.CaseSensitiveProxyTags(ctx));
|
return ctx.Execute<Config>(null, m => m.CaseSensitiveProxyTags(ctx));
|
||||||
if (ctx.MatchMultiple(new[] { "proxy" }, new[] { "error" }) || ctx.Match("pe"))
|
if (ctx.MatchMultiple(new[] { "proxy" }, new[] { "error" }) || ctx.Match("pe"))
|
||||||
return ctx.Execute<Config>(null, m => m.ProxyErrorMessageEnabled(ctx));
|
return ctx.Execute<Config>(null, m => m.ProxyErrorMessageEnabled(ctx));
|
||||||
|
if (ctx.MatchMultiple(new[] { "split" }, new[] { "ids" }) || ctx.Match("sid"))
|
||||||
|
return ctx.Execute<Config>(null, m => m.HidDisplaySplit(ctx));
|
||||||
|
|
||||||
// todo: maybe add the list of configuration keys here?
|
// todo: maybe add the list of configuration keys here?
|
||||||
return ctx.Reply($"{Emojis.Error} Could not find a setting with that name. Please see `pk;commands config` for the list of possible config settings.");
|
return ctx.Reply($"{Emojis.Error} Could not find a setting with that name. Please see `pk;commands config` for the list of possible config settings.");
|
||||||
|
|
|
||||||
|
|
@ -53,8 +53,10 @@ public static class ContextEntityArgumentsExt
|
||||||
return await ctx.Repository.GetSystemByAccount(id);
|
return await ctx.Repository.GetSystemByAccount(id);
|
||||||
|
|
||||||
// Finally, try HID parsing
|
// Finally, try HID parsing
|
||||||
var system = await ctx.Repository.GetSystemByHid(input);
|
if (input.TryParseHid(out var hid))
|
||||||
return system;
|
return await ctx.Repository.GetSystemByHid(hid);
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<PKMember> PeekMember(this Context ctx, SystemId? restrictToSystem = null)
|
public static async Task<PKMember> PeekMember(this Context ctx, SystemId? restrictToSystem = null)
|
||||||
|
|
@ -83,7 +85,7 @@ public static class ContextEntityArgumentsExt
|
||||||
|
|
||||||
// Finally (or if by-HID lookup is specified), check if input is a valid HID and then try member HID parsing:
|
// Finally (or if by-HID lookup is specified), check if input is a valid HID and then try member HID parsing:
|
||||||
|
|
||||||
if (!Regex.IsMatch(input, @"^[a-zA-Z]{5}$"))
|
if (!input.TryParseHid(out var hid))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
// For posterity:
|
// For posterity:
|
||||||
|
|
@ -94,21 +96,21 @@ public static class ContextEntityArgumentsExt
|
||||||
PKMember memberByHid = null;
|
PKMember memberByHid = null;
|
||||||
if (restrictToSystem != null)
|
if (restrictToSystem != null)
|
||||||
{
|
{
|
||||||
memberByHid = await ctx.Repository.GetMemberByHid(input, restrictToSystem);
|
memberByHid = await ctx.Repository.GetMemberByHid(hid, restrictToSystem);
|
||||||
if (memberByHid != null)
|
if (memberByHid != null)
|
||||||
return memberByHid;
|
return memberByHid;
|
||||||
}
|
}
|
||||||
// otherwise we try the querier's system and if that doesn't work we do global
|
// otherwise we try the querier's system and if that doesn't work we do global
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
memberByHid = await ctx.Repository.GetMemberByHid(input, ctx.System?.Id);
|
memberByHid = await ctx.Repository.GetMemberByHid(hid, ctx.System?.Id);
|
||||||
if (memberByHid != null)
|
if (memberByHid != null)
|
||||||
return memberByHid;
|
return memberByHid;
|
||||||
|
|
||||||
// ff ctx.System was null then this would be a duplicate of above and we don't want to run it again
|
// ff ctx.System was null then this would be a duplicate of above and we don't want to run it again
|
||||||
if (ctx.System != null)
|
if (ctx.System != null)
|
||||||
{
|
{
|
||||||
memberByHid = await ctx.Repository.GetMemberByHid(input);
|
memberByHid = await ctx.Repository.GetMemberByHid(hid);
|
||||||
if (memberByHid != null)
|
if (memberByHid != null)
|
||||||
return memberByHid;
|
return memberByHid;
|
||||||
}
|
}
|
||||||
|
|
@ -148,7 +150,10 @@ public static class ContextEntityArgumentsExt
|
||||||
return byDisplayName;
|
return byDisplayName;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (await ctx.Repository.GetGroupByHid(input, restrictToSystem) is { } byHid)
|
if (!input.TryParseHid(out var hid))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (await ctx.Repository.GetGroupByHid(hid, restrictToSystem) is { } byHid)
|
||||||
return byHid;
|
return byHid;
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -169,12 +174,12 @@ public static class ContextEntityArgumentsExt
|
||||||
{
|
{
|
||||||
if (input.Length == 5)
|
if (input.Length == 5)
|
||||||
return $"{entity} with ID \"{input}\" not found.";
|
return $"{entity} with ID \"{input}\" not found.";
|
||||||
return $"{entity} not found. Note that a {entity.ToLower()} ID is 5 characters long.";
|
return $"{entity} not found. Note that a {entity.ToLower()} ID is 5 or 6 characters long.";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input.Length == 5)
|
if (input.Length == 5)
|
||||||
return $"{entity} with ID or name \"{input}\" not found.";
|
return $"{entity} with ID or name \"{input}\" not found.";
|
||||||
return $"{entity} with name \"{input}\" not found. Note that a {entity.ToLower()} ID is 5 characters long.";
|
return $"{entity} with name \"{input}\" not found. Note that a {entity.ToLower()} ID is 5 or 6 characters long.";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<Channel> MatchChannel(this Context ctx)
|
public static async Task<Channel> MatchChannel(this Context ctx)
|
||||||
|
|
|
||||||
|
|
@ -29,9 +29,9 @@ public class Admin
|
||||||
if (target == null)
|
if (target == null)
|
||||||
throw new PKError("Unknown system.");
|
throw new PKError("Unknown system.");
|
||||||
|
|
||||||
var newHid = ctx.PopArgument();
|
var input = ctx.PopArgument();
|
||||||
if (!Regex.IsMatch(newHid, "^[a-z]{5}$"))
|
if (!input.TryParseHid(out var newHid))
|
||||||
throw new PKError($"Invalid new system ID `{newHid}`.");
|
throw new PKError($"Invalid new system ID `{input}`.");
|
||||||
|
|
||||||
var existingSystem = await ctx.Repository.GetSystemByHid(newHid);
|
var existingSystem = await ctx.Repository.GetSystemByHid(newHid);
|
||||||
if (existingSystem != null)
|
if (existingSystem != null)
|
||||||
|
|
@ -52,9 +52,9 @@ public class Admin
|
||||||
if (target == null)
|
if (target == null)
|
||||||
throw new PKError("Unknown member.");
|
throw new PKError("Unknown member.");
|
||||||
|
|
||||||
var newHid = ctx.PopArgument();
|
var input = ctx.PopArgument();
|
||||||
if (!Regex.IsMatch(newHid, "^[a-z]{5}$"))
|
if (!input.TryParseHid(out var newHid))
|
||||||
throw new PKError($"Invalid new member ID `{newHid}`.");
|
throw new PKError($"Invalid new member ID `{input}`.");
|
||||||
|
|
||||||
var existingMember = await ctx.Repository.GetMemberByHid(newHid);
|
var existingMember = await ctx.Repository.GetMemberByHid(newHid);
|
||||||
if (existingMember != null)
|
if (existingMember != null)
|
||||||
|
|
@ -78,9 +78,9 @@ public class Admin
|
||||||
if (target == null)
|
if (target == null)
|
||||||
throw new PKError("Unknown group.");
|
throw new PKError("Unknown group.");
|
||||||
|
|
||||||
var newHid = ctx.PopArgument();
|
var input = ctx.PopArgument();
|
||||||
if (!Regex.IsMatch(newHid, "^[a-z]{5}$"))
|
if (!input.TryParseHid(out var newHid))
|
||||||
throw new PKError($"Invalid new group ID `{newHid}`.");
|
throw new PKError($"Invalid new group ID `{input}`.");
|
||||||
|
|
||||||
var existingGroup = await ctx.Repository.GetGroupByHid(newHid);
|
var existingGroup = await ctx.Repository.GetGroupByHid(newHid);
|
||||||
if (existingGroup != null)
|
if (existingGroup != null)
|
||||||
|
|
|
||||||
|
|
@ -130,7 +130,7 @@ public class Autoproxy
|
||||||
{
|
{
|
||||||
if (relevantMember == null)
|
if (relevantMember == null)
|
||||||
throw new ArgumentException("Attempted to print member autoproxy status, but the linked member ID wasn't found in the database. Should be handled appropriately.");
|
throw new ArgumentException("Attempted to print member autoproxy status, but the linked member ID wasn't found in the database. Should be handled appropriately.");
|
||||||
eb.Description($"Autoproxy is currently set to **front mode** in this server. The current (first) fronter is **{relevantMember.NameFor(ctx).EscapeMarkdown()}** (`{relevantMember.Hid}`). To disable, type `pk;autoproxy off`.");
|
eb.Description($"Autoproxy is currently set to **front mode** in this server. The current (first) fronter is **{relevantMember.NameFor(ctx).EscapeMarkdown()}** (`{relevantMember.DisplayHid(ctx.Config)}`). To disable, type `pk;autoproxy off`.");
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
@ -142,7 +142,7 @@ public class Autoproxy
|
||||||
// ideally we would set it to off in the database though...
|
// ideally we would set it to off in the database though...
|
||||||
eb.Description($"Autoproxy is currently **off** in this server. To enable it, use one of the following commands:\n{commandList}");
|
eb.Description($"Autoproxy is currently **off** in this server. To enable it, use one of the following commands:\n{commandList}");
|
||||||
else
|
else
|
||||||
eb.Description($"Autoproxy is active for member **{relevantMember.NameFor(ctx)}** (`{relevantMember.Hid}`) in this server. To disable, type `pk;autoproxy off`.");
|
eb.Description($"Autoproxy is active for member **{relevantMember.NameFor(ctx)}** (`{relevantMember.DisplayHid(ctx.Config)}`) in this server. To disable, type `pk;autoproxy off`.");
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -150,7 +150,7 @@ public class Autoproxy
|
||||||
if (relevantMember == null)
|
if (relevantMember == null)
|
||||||
eb.Description("Autoproxy is currently set to **latch mode**, meaning the *last-proxied member* will be autoproxied. **No member is currently latched.** To disable, type `pk;autoproxy off`.");
|
eb.Description("Autoproxy is currently set to **latch mode**, meaning the *last-proxied member* will be autoproxied. **No member is currently latched.** To disable, type `pk;autoproxy off`.");
|
||||||
else
|
else
|
||||||
eb.Description($"Autoproxy is currently set to **latch mode**, meaning the *last-proxied member* will be autoproxied. The currently latched member is **{relevantMember.NameFor(ctx)}** (`{relevantMember.Hid}`). To disable, type `pk;autoproxy off`.");
|
eb.Description($"Autoproxy is currently set to **latch mode**, meaning the *last-proxied member* will be autoproxied. The currently latched member is **{relevantMember.NameFor(ctx)}** (`{relevantMember.DisplayHid(ctx.Config)}`). To disable, type `pk;autoproxy off`.");
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -102,6 +102,13 @@ public class Config
|
||||||
"enabled"
|
"enabled"
|
||||||
));
|
));
|
||||||
|
|
||||||
|
items.Add(new(
|
||||||
|
"Split IDs",
|
||||||
|
"Whether to display 6-character IDs split with a hyphen, to ease readability",
|
||||||
|
EnabledDisabled(ctx.Config.HidDisplaySplit),
|
||||||
|
"disabled"
|
||||||
|
));
|
||||||
|
|
||||||
await ctx.Paginate<PaginatedConfigItem>(
|
await ctx.Paginate<PaginatedConfigItem>(
|
||||||
items.ToAsyncEnumerable(),
|
items.ToAsyncEnumerable(),
|
||||||
items.Count,
|
items.Count,
|
||||||
|
|
@ -443,4 +450,18 @@ public class Config
|
||||||
await ctx.Reply("Proxy error messages are now disabled. Messages that fail to proxy (due to message or attachment size) will not throw an error message.");
|
await ctx.Reply("Proxy error messages are now disabled. Messages that fail to proxy (due to message or attachment size) will not throw an error message.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task HidDisplaySplit(Context ctx)
|
||||||
|
{
|
||||||
|
if (!ctx.HasNext())
|
||||||
|
{
|
||||||
|
var msg = $"Splitting of 6-character IDs with a hyphen is currently **{EnabledDisabled(ctx.Config.HidDisplaySplit)}**.";
|
||||||
|
await ctx.Reply(msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var newVal = ctx.MatchToggle(true);
|
||||||
|
await ctx.Repository.UpdateSystemConfig(ctx.System.Id, new() { HidDisplaySplit = newVal });
|
||||||
|
await ctx.Reply($"Splitting of 6-character IDs with a hyphen is now {EnabledDisabled(newVal)}.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -66,7 +66,7 @@ public class GroupMember
|
||||||
if (groups.Count == 0)
|
if (groups.Count == 0)
|
||||||
description = "This member has no groups.";
|
description = "This member has no groups.";
|
||||||
else
|
else
|
||||||
description = string.Join("\n", groups.Select(g => $"[`{g.Hid}`] **{g.DisplayName ?? g.Name}**"));
|
description = string.Join("\n", groups.Select(g => $"[`{g.DisplayHid(ctx.Config)}`] **{g.DisplayName ?? g.Name}**"));
|
||||||
|
|
||||||
if (pctx == LookupContext.ByOwner)
|
if (pctx == LookupContext.ByOwner)
|
||||||
{
|
{
|
||||||
|
|
@ -130,23 +130,23 @@ public class GroupMember
|
||||||
var opts = ctx.ParseListOptions(ctx.DirectLookupContextFor(target.System));
|
var opts = ctx.ParseListOptions(ctx.DirectLookupContextFor(target.System));
|
||||||
opts.GroupFilter = target.Id;
|
opts.GroupFilter = target.Id;
|
||||||
|
|
||||||
var title = new StringBuilder($"Members of {target.DisplayName ?? target.Name} (`{target.Hid}`) in ");
|
var title = new StringBuilder($"Members of {target.DisplayName ?? target.Name} (`{target.DisplayHid(ctx.Config)}`) in ");
|
||||||
if (ctx.Guild != null)
|
if (ctx.Guild != null)
|
||||||
{
|
{
|
||||||
var guildSettings = await ctx.Repository.GetSystemGuild(ctx.Guild.Id, targetSystem.Id);
|
var guildSettings = await ctx.Repository.GetSystemGuild(ctx.Guild.Id, targetSystem.Id);
|
||||||
if (guildSettings.DisplayName != null)
|
if (guildSettings.DisplayName != null)
|
||||||
title.Append($"{guildSettings.DisplayName} (`{targetSystem.Hid}`)");
|
title.Append($"{guildSettings.DisplayName} (`{targetSystem.DisplayHid(ctx.Config)}`)");
|
||||||
else if (targetSystem.NameFor(ctx) != null)
|
else if (targetSystem.NameFor(ctx) != null)
|
||||||
title.Append($"{targetSystem.NameFor(ctx)} (`{targetSystem.Hid}`)");
|
title.Append($"{targetSystem.NameFor(ctx)} (`{targetSystem.DisplayHid(ctx.Config)}`)");
|
||||||
else
|
else
|
||||||
title.Append($"`{targetSystem.Hid}`");
|
title.Append($"`{targetSystem.DisplayHid(ctx.Config)}`");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (targetSystem.NameFor(ctx) != null)
|
if (targetSystem.NameFor(ctx) != null)
|
||||||
title.Append($"{targetSystem.NameFor(ctx)} (`{targetSystem.Hid}`)");
|
title.Append($"{targetSystem.NameFor(ctx)} (`{targetSystem.DisplayHid(ctx.Config)}`)");
|
||||||
else
|
else
|
||||||
title.Append($"`{targetSystem.Hid}`");
|
title.Append($"`{targetSystem.DisplayHid(ctx.Config)}`");
|
||||||
}
|
}
|
||||||
if (opts.Search != null)
|
if (opts.Search != null)
|
||||||
title.Append($" matching **{opts.Search.Truncate(100)}**");
|
title.Append($" matching **{opts.Search.Truncate(100)}**");
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ public class Groups
|
||||||
if (existingGroup != null)
|
if (existingGroup != null)
|
||||||
{
|
{
|
||||||
var msg =
|
var msg =
|
||||||
$"{Emojis.Warn} You already have a group in your system with the name \"{existingGroup.Name}\" (with ID `{existingGroup.Hid}`). Do you want to create another group with the same name?";
|
$"{Emojis.Warn} You already have a group in your system with the name \"{existingGroup.Name}\" (with ID `{existingGroup.DisplayHid(ctx.Config)}`). Do you want to create another group with the same name?";
|
||||||
if (!await ctx.PromptYesNo(msg, "Create"))
|
if (!await ctx.PromptYesNo(msg, "Create"))
|
||||||
throw new PKError("Group creation cancelled.");
|
throw new PKError("Group creation cancelled.");
|
||||||
}
|
}
|
||||||
|
|
@ -83,7 +83,7 @@ public class Groups
|
||||||
|
|
||||||
var eb = new EmbedBuilder()
|
var eb = new EmbedBuilder()
|
||||||
.Description(
|
.Description(
|
||||||
$"Your new group, **{groupName}**, has been created, with the group ID **`{newGroup.Hid}`**.\nBelow are a couple of useful commands:")
|
$"Your new group, **{groupName}**, has been created, with the group ID **`{newGroup.DisplayHid(ctx.Config)}`**.\nBelow are a couple of useful commands:")
|
||||||
.Field(new Embed.Field("View the group card", $"> pk;group **{reference}**"))
|
.Field(new Embed.Field("View the group card", $"> pk;group **{reference}**"))
|
||||||
.Field(new Embed.Field("Add members to the group",
|
.Field(new Embed.Field("Add members to the group",
|
||||||
$"> pk;group **{reference}** add **MemberName**\n> pk;group **{reference}** add **Member1** **Member2** **Member3** (and so on...)"))
|
$"> pk;group **{reference}** add **MemberName**\n> pk;group **{reference}** add **Member1** **Member2** **Member3** (and so on...)"))
|
||||||
|
|
@ -113,7 +113,7 @@ public class Groups
|
||||||
if (existingGroup != null && existingGroup.Id != target.Id)
|
if (existingGroup != null && existingGroup.Id != target.Id)
|
||||||
{
|
{
|
||||||
var msg =
|
var msg =
|
||||||
$"{Emojis.Warn} You already have a group in your system with the name \"{existingGroup.Name}\" (with ID `{existingGroup.Hid}`). Do you want to rename this group to that name too?";
|
$"{Emojis.Warn} You already have a group in your system with the name \"{existingGroup.Name}\" (with ID `{existingGroup.DisplayHid(ctx.Config)}`). Do you want to rename this group to that name too?";
|
||||||
if (!await ctx.PromptYesNo(msg, "Rename"))
|
if (!await ctx.PromptYesNo(msg, "Rename"))
|
||||||
throw new PKError("Group rename cancelled.");
|
throw new PKError("Group rename cancelled.");
|
||||||
}
|
}
|
||||||
|
|
@ -450,20 +450,20 @@ public class Groups
|
||||||
await ctx.RenderGroupList(
|
await ctx.RenderGroupList(
|
||||||
ctx.LookupContextFor(system.Id),
|
ctx.LookupContextFor(system.Id),
|
||||||
system.Id,
|
system.Id,
|
||||||
GetEmbedTitle(system, opts),
|
GetEmbedTitle(ctx, system, opts),
|
||||||
system.Color,
|
system.Color,
|
||||||
opts
|
opts
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetEmbedTitle(PKSystem target, ListOptions opts)
|
private string GetEmbedTitle(Context ctx, PKSystem target, ListOptions opts)
|
||||||
{
|
{
|
||||||
var title = new StringBuilder("Groups of ");
|
var title = new StringBuilder("Groups of ");
|
||||||
|
|
||||||
if (target.Name != null)
|
if (target.Name != null)
|
||||||
title.Append($"{target.Name} (`{target.Hid}`)");
|
title.Append($"{target.Name} (`{target.DisplayHid(ctx.Config)}`)");
|
||||||
else
|
else
|
||||||
title.Append($"`{target.Hid}`");
|
title.Append($"`{target.DisplayHid(ctx.Config)}`");
|
||||||
|
|
||||||
if (opts.Search != null)
|
if (opts.Search != null)
|
||||||
title.Append($" matching **{opts.Search}**");
|
title.Append($" matching **{opts.Search}**");
|
||||||
|
|
@ -586,7 +586,7 @@ public class Groups
|
||||||
|
|
||||||
public async Task DisplayId(Context ctx, PKGroup target)
|
public async Task DisplayId(Context ctx, PKGroup target)
|
||||||
{
|
{
|
||||||
await ctx.Reply(target.Hid);
|
await ctx.Reply(target.DisplayHid(ctx.Config));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<PKSystem> GetGroupSystem(Context ctx, PKGroup target)
|
private async Task<PKSystem> GetGroupSystem(Context ctx, PKGroup target)
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,7 @@ public static class ContextListExt
|
||||||
// so run it through a helper that "makes it work" :)
|
// so run it through a helper that "makes it work" :)
|
||||||
eb.WithSimpleLineContent(page.Select(m =>
|
eb.WithSimpleLineContent(page.Select(m =>
|
||||||
{
|
{
|
||||||
var ret = $"[`{m.Hid}`] **{m.NameFor(ctx)}** ";
|
var ret = $"[`{m.DisplayHid(ctx.Config)}`] **{m.NameFor(ctx)}** ";
|
||||||
|
|
||||||
if (opts.IncludeMessageCount && m.MessageCountFor(lookupCtx) is { } count)
|
if (opts.IncludeMessageCount && m.MessageCountFor(lookupCtx) is { } count)
|
||||||
ret += $"({count} messages)";
|
ret += $"({count} messages)";
|
||||||
|
|
@ -162,7 +162,7 @@ public static class ContextListExt
|
||||||
{
|
{
|
||||||
foreach (var m in page)
|
foreach (var m in page)
|
||||||
{
|
{
|
||||||
var profile = new StringBuilder($"**ID**: {m.Hid}");
|
var profile = new StringBuilder($"**ID**: {m.DisplayHid(ctx.Config)}");
|
||||||
|
|
||||||
if (m.DisplayName != null && m.NamePrivacy.CanAccess(lookupCtx))
|
if (m.DisplayName != null && m.NamePrivacy.CanAccess(lookupCtx))
|
||||||
profile.Append($"\n**Display name**: {m.DisplayName}");
|
profile.Append($"\n**Display name**: {m.DisplayName}");
|
||||||
|
|
@ -238,7 +238,7 @@ public static class ContextListExt
|
||||||
// so run it through a helper that "makes it work" :)
|
// so run it through a helper that "makes it work" :)
|
||||||
eb.WithSimpleLineContent(page.Select(g =>
|
eb.WithSimpleLineContent(page.Select(g =>
|
||||||
{
|
{
|
||||||
var ret = $"[`{g.Hid}`] **{g.NameFor(ctx)}** ";
|
var ret = $"[`{g.DisplayHid(ctx.Config)}`] **{g.NameFor(ctx)}** ";
|
||||||
|
|
||||||
switch (opts.SortProperty)
|
switch (opts.SortProperty)
|
||||||
{
|
{
|
||||||
|
|
@ -308,7 +308,7 @@ public static class ContextListExt
|
||||||
{
|
{
|
||||||
foreach (var g in page)
|
foreach (var g in page)
|
||||||
{
|
{
|
||||||
var profile = new StringBuilder($"**ID**: {g.Hid}");
|
var profile = new StringBuilder($"**ID**: {g.DisplayHid(ctx.Config)}");
|
||||||
|
|
||||||
if (g.DisplayName != null && g.NamePrivacy.CanAccess(lookupCtx))
|
if (g.DisplayName != null && g.NamePrivacy.CanAccess(lookupCtx))
|
||||||
profile.Append($"\n**Display name**: {g.DisplayName}");
|
profile.Append($"\n**Display name**: {g.DisplayName}");
|
||||||
|
|
|
||||||
|
|
@ -101,12 +101,12 @@ public class Member
|
||||||
|
|
||||||
// Send confirmation and space hint
|
// Send confirmation and space hint
|
||||||
await ctx.Reply(
|
await ctx.Reply(
|
||||||
$"{Emojis.Success} Member \"{memberName}\" (`{member.Hid}`) registered! Check out the getting started page for how to get a member up and running: https://pluralkit.me/start#create-a-member");
|
$"{Emojis.Success} Member \"{memberName}\" (`{member.DisplayHid(ctx.Config)}`) registered! Check out the getting started page for how to get a member up and running: https://pluralkit.me/start#create-a-member");
|
||||||
// todo: move this to ModelRepository
|
// todo: move this to ModelRepository
|
||||||
if (await ctx.Database.Execute(conn => conn.QuerySingleAsync<bool>("select has_private_members(@System)",
|
if (await ctx.Database.Execute(conn => conn.QuerySingleAsync<bool>("select has_private_members(@System)",
|
||||||
new { System = ctx.System.Id })) && !ctx.Config.MemberDefaultPrivate) //if has private members
|
new { System = ctx.System.Id })) && !ctx.Config.MemberDefaultPrivate) //if has private members
|
||||||
await ctx.Reply(
|
await ctx.Reply(
|
||||||
$"{Emojis.Warn} This member is currently **public**. To change this, use `pk;member {member.Hid} private`.");
|
$"{Emojis.Warn} This member is currently **public**. To change this, use `pk;member {member.DisplayHid(ctx.Config)} private`.");
|
||||||
if (avatarArg != null)
|
if (avatarArg != null)
|
||||||
if (imageMatchError == null)
|
if (imageMatchError == null)
|
||||||
await ctx.Reply(
|
await ctx.Reply(
|
||||||
|
|
@ -115,7 +115,7 @@ public class Member
|
||||||
await ctx.Reply($"{Emojis.Error} Couldn't set avatar: {imageMatchError.Message}");
|
await ctx.Reply($"{Emojis.Error} Couldn't set avatar: {imageMatchError.Message}");
|
||||||
if (memberName.Contains(" "))
|
if (memberName.Contains(" "))
|
||||||
await ctx.Reply(
|
await ctx.Reply(
|
||||||
$"{Emojis.Note} Note that this member's name contains spaces. You will need to surround it with \"double quotes\" when using commands referring to it, or just use the member's 5-character ID (which is `{member.Hid}`).");
|
$"{Emojis.Note} Note that this member's name contains spaces. You will need to surround it with \"double quotes\" when using commands referring to it, or just use the member's short ID (which is `{member.DisplayHid(ctx.Config)}`).");
|
||||||
if (memberCount >= memberLimit)
|
if (memberCount >= memberLimit)
|
||||||
await ctx.Reply(
|
await ctx.Reply(
|
||||||
$"{Emojis.Warn} You have reached the per-system member limit ({memberLimit}). If you need to add more members, you can either delete existing members, or ask for your limit to be raised in the PluralKit support server: <https://discord.gg/PczBt78>");
|
$"{Emojis.Warn} You have reached the per-system member limit ({memberLimit}). If you need to add more members, you can either delete existing members, or ask for your limit to be raised in the PluralKit support server: <https://discord.gg/PczBt78>");
|
||||||
|
|
@ -128,7 +128,7 @@ public class Member
|
||||||
{
|
{
|
||||||
var system = await ctx.Repository.GetSystem(target.System);
|
var system = await ctx.Repository.GetSystem(target.System);
|
||||||
await ctx.Reply(
|
await ctx.Reply(
|
||||||
embed: await _embeds.CreateMemberEmbed(system, target, ctx.Guild, ctx.LookupContextFor(system.Id), ctx.Zone));
|
embed: await _embeds.CreateMemberEmbed(system, target, ctx.Guild, ctx.Config, ctx.LookupContextFor(system.Id), ctx.Zone));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Soulscream(Context ctx, PKMember target)
|
public async Task Soulscream(Context ctx, PKMember target)
|
||||||
|
|
@ -156,6 +156,6 @@ public class Member
|
||||||
|
|
||||||
public async Task DisplayId(Context ctx, PKMember target)
|
public async Task DisplayId(Context ctx, PKMember target)
|
||||||
{
|
{
|
||||||
await ctx.Reply(target.Hid);
|
await ctx.Reply(target.DisplayHid(ctx.Config));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -36,7 +36,7 @@ public class MemberEdit
|
||||||
if (existingMember != null && existingMember.Id != target.Id)
|
if (existingMember != null && existingMember.Id != target.Id)
|
||||||
{
|
{
|
||||||
var msg =
|
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?";
|
$"{Emojis.Warn} You already have a member in your system with the name \"{existingMember.NameFor(ctx)}\" (`{existingMember.DisplayHid(ctx.Config)}`). Do you want to rename this member to that name too?";
|
||||||
if (!await ctx.PromptYesNo(msg, "Rename")) throw new PKError("Member renaming cancelled.");
|
if (!await ctx.PromptYesNo(msg, "Rename")) throw new PKError("Member renaming cancelled.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -47,7 +47,7 @@ public class MemberEdit
|
||||||
await ctx.Reply($"{Emojis.Success} Member renamed (using {newName.Length}/{Limits.MaxMemberNameLength} characters).");
|
await ctx.Reply($"{Emojis.Success} Member renamed (using {newName.Length}/{Limits.MaxMemberNameLength} characters).");
|
||||||
if (newName.Contains(" "))
|
if (newName.Contains(" "))
|
||||||
await ctx.Reply(
|
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, or just use the member's 5-character ID (which is `{target.Hid}`).");
|
$"{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, or just use the member's short ID (which is `{target.DisplayHid(ctx.Config)}`).");
|
||||||
if (target.DisplayName != null)
|
if (target.DisplayName != null)
|
||||||
await ctx.Reply(
|
await ctx.Reply(
|
||||||
$"{Emojis.Note} Note that this member has a display name set ({target.DisplayName}), and will be proxied using that name instead.");
|
$"{Emojis.Note} Note that this member has a display name set ({target.DisplayName}), and will be proxied using that name instead.");
|
||||||
|
|
@ -211,7 +211,7 @@ public class MemberEdit
|
||||||
var eb = new EmbedBuilder()
|
var eb = new EmbedBuilder()
|
||||||
.Title($"{target.NameFor(ctx)}'s banner image")
|
.Title($"{target.NameFor(ctx)}'s banner image")
|
||||||
.Image(new Embed.EmbedImage(target.BannerImage))
|
.Image(new Embed.EmbedImage(target.BannerImage))
|
||||||
.Description($"To clear, use `pk;member {target.Hid} banner clear`.");
|
.Description($"To clear, use `pk;member {target.Reference(ctx)} banner clear`.");
|
||||||
await ctx.Reply(embed: eb.Build());
|
await ctx.Reply(embed: eb.Build());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ public class Random
|
||||||
|
|
||||||
var randInt = randGen.Next(members.Count);
|
var randInt = randGen.Next(members.Count);
|
||||||
await ctx.Reply(embed: await _embeds.CreateMemberEmbed(target, members[randInt], ctx.Guild,
|
await ctx.Reply(embed: await _embeds.CreateMemberEmbed(target, members[randInt], ctx.Guild,
|
||||||
ctx.LookupContextFor(target.Id), ctx.Zone));
|
ctx.Config, ctx.LookupContextFor(target.Id), ctx.Zone));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Group(Context ctx, PKSystem target)
|
public async Task Group(Context ctx, PKSystem target)
|
||||||
|
|
@ -93,6 +93,6 @@ public class Random
|
||||||
|
|
||||||
var randInt = randGen.Next(ms.Count);
|
var randInt = randGen.Next(ms.Count);
|
||||||
await ctx.Reply(embed: await _embeds.CreateMemberEmbed(system, ms[randInt], ctx.Guild,
|
await ctx.Reply(embed: await _embeds.CreateMemberEmbed(system, ms[randInt], ctx.Guild,
|
||||||
ctx.LookupContextFor(group.System), ctx.Zone));
|
ctx.Config, ctx.LookupContextFor(group.System), ctx.Zone));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -39,6 +39,6 @@ public class System
|
||||||
if (target == null)
|
if (target == null)
|
||||||
throw Errors.NoSystemError;
|
throw Errors.NoSystemError;
|
||||||
|
|
||||||
await ctx.Reply(target.Hid);
|
await ctx.Reply(target.DisplayHid(ctx.Config));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -33,11 +33,11 @@ public class SystemList
|
||||||
|
|
||||||
var systemGuildSettings = ctx.Guild != null ? await ctx.Repository.GetSystemGuild(ctx.Guild.Id, target.Id) : null;
|
var systemGuildSettings = ctx.Guild != null ? await ctx.Repository.GetSystemGuild(ctx.Guild.Id, target.Id) : null;
|
||||||
if (systemGuildSettings != null && systemGuildSettings.DisplayName != null)
|
if (systemGuildSettings != null && systemGuildSettings.DisplayName != null)
|
||||||
title.Append($"{systemGuildSettings.DisplayName} (`{target.Hid}`)");
|
title.Append($"{systemGuildSettings.DisplayName} (`{target.DisplayHid(ctx.Config)}`)");
|
||||||
else if (target.NameFor(ctx) != null)
|
else if (target.NameFor(ctx) != null)
|
||||||
title.Append($"{target.NameFor(ctx)} (`{target.Hid}`)");
|
title.Append($"{target.NameFor(ctx)} (`{target.DisplayHid(ctx.Config)}`)");
|
||||||
else
|
else
|
||||||
title.Append($"`{target.Hid}`");
|
title.Append($"`{target.DisplayHid(ctx.Config)}`");
|
||||||
|
|
||||||
if (opts.Search != null)
|
if (opts.Search != null)
|
||||||
title.Append($" matching **{opts.Search.Truncate(100)}**");
|
title.Append($" matching **{opts.Search.Truncate(100)}**");
|
||||||
|
|
|
||||||
|
|
@ -16,20 +16,23 @@ public class InteractionCreated: IEventHandler<InteractionCreateEvent>
|
||||||
private readonly ApplicationCommandTree _commandTree;
|
private readonly ApplicationCommandTree _commandTree;
|
||||||
private readonly ILifetimeScope _services;
|
private readonly ILifetimeScope _services;
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
|
private readonly ModelRepository _repo;
|
||||||
|
|
||||||
public InteractionCreated(InteractionDispatchService interactionDispatch, ApplicationCommandTree commandTree,
|
public InteractionCreated(InteractionDispatchService interactionDispatch, ApplicationCommandTree commandTree,
|
||||||
ILifetimeScope services, ILogger logger)
|
ILifetimeScope services, ILogger logger, ModelRepository repo)
|
||||||
{
|
{
|
||||||
_interactionDispatch = interactionDispatch;
|
_interactionDispatch = interactionDispatch;
|
||||||
_commandTree = commandTree;
|
_commandTree = commandTree;
|
||||||
_services = services;
|
_services = services;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
_repo = repo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Handle(int shardId, InteractionCreateEvent evt)
|
public async Task Handle(int shardId, InteractionCreateEvent evt)
|
||||||
{
|
{
|
||||||
var system = await _services.Resolve<ModelRepository>().GetSystemByAccount(evt.Member?.User.Id ?? evt.User!.Id);
|
var system = await _repo.GetSystemByAccount(evt.Member?.User.Id ?? evt.User!.Id);
|
||||||
var ctx = new InteractionContext(_services, evt, system);
|
var config = system != null ? await _repo.GetSystemConfig(system!.Id) : null;
|
||||||
|
var ctx = new InteractionContext(_services, evt, system, config);
|
||||||
|
|
||||||
switch (evt.Type)
|
switch (evt.Type)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -175,6 +175,8 @@ public class ReactionAdded: IEventHandler<MessageReactionAddEvent>
|
||||||
private async ValueTask HandleQueryReaction(MessageReactionAddEvent evt, FullMessage msg)
|
private async ValueTask HandleQueryReaction(MessageReactionAddEvent evt, FullMessage msg)
|
||||||
{
|
{
|
||||||
var guild = await _cache.GetGuild(evt.GuildId!.Value);
|
var guild = await _cache.GetGuild(evt.GuildId!.Value);
|
||||||
|
var system = await _repo.GetSystemByAccount(evt.UserId);
|
||||||
|
var config = system != null ? await _repo.GetSystemConfig(system.Id) : null;
|
||||||
|
|
||||||
// Try to DM the user info about the message
|
// Try to DM the user info about the message
|
||||||
try
|
try
|
||||||
|
|
@ -188,11 +190,12 @@ public class ReactionAdded: IEventHandler<MessageReactionAddEvent>
|
||||||
msg.System,
|
msg.System,
|
||||||
msg.Member,
|
msg.Member,
|
||||||
guild,
|
guild,
|
||||||
|
config,
|
||||||
LookupContext.ByNonOwner,
|
LookupContext.ByNonOwner,
|
||||||
DateTimeZone.Utc
|
DateTimeZone.Utc
|
||||||
));
|
));
|
||||||
|
|
||||||
embeds.Add(await _embeds.CreateMessageInfoEmbed(msg, true));
|
embeds.Add(await _embeds.CreateMessageInfoEmbed(msg, true, config));
|
||||||
|
|
||||||
await _rest.CreateMessage(dm, new MessageRequest { Embeds = embeds.ToArray() });
|
await _rest.CreateMessage(dm, new MessageRequest { Embeds = embeds.ToArray() });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ public class EmbedService
|
||||||
var eb = new EmbedBuilder()
|
var eb = new EmbedBuilder()
|
||||||
.Title(system.NameFor(ctx))
|
.Title(system.NameFor(ctx))
|
||||||
.Footer(new Embed.EmbedFooter(
|
.Footer(new Embed.EmbedFooter(
|
||||||
$"System ID: {system.Hid} | Created on {system.Created.FormatZoned(cctx.Zone)}"))
|
$"System ID: {system.DisplayHid(cctx.Config)} | Created on {system.Created.FormatZoned(cctx.Zone)}"))
|
||||||
.Color(color)
|
.Color(color)
|
||||||
.Url($"https://dash.pluralkit.me/profile/s/{system.Hid}");
|
.Url($"https://dash.pluralkit.me/profile/s/{system.Hid}");
|
||||||
|
|
||||||
|
|
@ -90,7 +90,7 @@ public class EmbedService
|
||||||
{
|
{
|
||||||
var memberStr = string.Join(", ", switchMembers.Select(m => m.NameFor(ctx)));
|
var memberStr = string.Join(", ", switchMembers.Select(m => m.NameFor(ctx)));
|
||||||
if (memberStr.Length > 200)
|
if (memberStr.Length > 200)
|
||||||
memberStr = $"[too many to show, see `pk;system {system.Hid} fronters`]";
|
memberStr = $"[too many to show, see `pk;system {system.DisplayHid(cctx.Config)} fronters`]";
|
||||||
eb.Field(new Embed.Field("Fronter".ToQuantity(switchMembers.Count, ShowQuantityAs.None), memberStr));
|
eb.Field(new Embed.Field("Fronter".ToQuantity(switchMembers.Count, ShowQuantityAs.None), memberStr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -137,7 +137,7 @@ public class EmbedService
|
||||||
{
|
{
|
||||||
if (memberCount > 0)
|
if (memberCount > 0)
|
||||||
eb.Field(new Embed.Field($"Members ({memberCount})",
|
eb.Field(new Embed.Field($"Members ({memberCount})",
|
||||||
$"(see `pk;system {system.Hid} list` or `pk;system {system.Hid} list full`)", true));
|
$"(see `pk;system {system.DisplayHid(cctx.Config)} list` or `pk;system {system.DisplayHid(cctx.Config)} list full`)", true));
|
||||||
else
|
else
|
||||||
eb.Field(new Embed.Field($"Members ({memberCount})", "Add one with `pk;member new`!", true));
|
eb.Field(new Embed.Field($"Members ({memberCount})", "Add one with `pk;member new`!", true));
|
||||||
}
|
}
|
||||||
|
|
@ -175,7 +175,7 @@ public class EmbedService
|
||||||
return embed.Build();
|
return embed.Build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Embed> CreateMemberEmbed(PKSystem system, PKMember member, Guild guild, LookupContext ctx, DateTimeZone zone)
|
public async Task<Embed> CreateMemberEmbed(PKSystem system, PKMember member, Guild guild, SystemConfig? ccfg, LookupContext ctx, DateTimeZone zone)
|
||||||
{
|
{
|
||||||
// string FormatTimestamp(Instant timestamp) => DateTimeFormats.ZonedDateTimeFormat.Format(timestamp.InZone(system.Zone));
|
// string FormatTimestamp(Instant timestamp) => DateTimeFormats.ZonedDateTimeFormat.Format(timestamp.InZone(system.Zone));
|
||||||
|
|
||||||
|
|
@ -216,7 +216,7 @@ public class EmbedService
|
||||||
// .WithColor(member.ColorPrivacy.CanAccess(ctx) ? color : DiscordUtils.Gray)
|
// .WithColor(member.ColorPrivacy.CanAccess(ctx) ? color : DiscordUtils.Gray)
|
||||||
.Color(color)
|
.Color(color)
|
||||||
.Footer(new Embed.EmbedFooter(
|
.Footer(new Embed.EmbedFooter(
|
||||||
$"System ID: {system.Hid} | Member ID: {member.Hid} {(member.MetadataPrivacy.CanAccess(ctx) ? $"| Created on {member.Created.FormatZoned(zone)}" : "")}"));
|
$"System ID: {system.DisplayHid(ccfg)} | Member ID: {member.DisplayHid(ccfg)} {(member.MetadataPrivacy.CanAccess(ctx) ? $"| Created on {member.Created.FormatZoned(zone)}" : "")}"));
|
||||||
|
|
||||||
if (member.DescriptionPrivacy.CanAccess(ctx))
|
if (member.DescriptionPrivacy.CanAccess(ctx))
|
||||||
eb.Image(new Embed.EmbedImage(member.BannerImage));
|
eb.Image(new Embed.EmbedImage(member.BannerImage));
|
||||||
|
|
@ -304,7 +304,7 @@ public class EmbedService
|
||||||
.Author(new Embed.EmbedAuthor(nameField, IconUrl: target.IconFor(pctx), Url: $"https://dash.pluralkit.me/profile/g/{target.Hid}"))
|
.Author(new Embed.EmbedAuthor(nameField, IconUrl: target.IconFor(pctx), Url: $"https://dash.pluralkit.me/profile/g/{target.Hid}"))
|
||||||
.Color(color);
|
.Color(color);
|
||||||
|
|
||||||
eb.Footer(new Embed.EmbedFooter($"System ID: {system.Hid} | Group ID: {target.Hid}{(target.MetadataPrivacy.CanAccess(pctx) ? $" | Created on {target.Created.FormatZoned(ctx.Zone)}" : "")}"));
|
eb.Footer(new Embed.EmbedFooter($"System ID: {system.DisplayHid(ctx.Config)} | Group ID: {target.DisplayHid(ctx.Config)}{(target.MetadataPrivacy.CanAccess(pctx) ? $" | Created on {target.Created.FormatZoned(ctx.Zone)}" : "")}"));
|
||||||
|
|
||||||
if (target.DescriptionPrivacy.CanAccess(pctx))
|
if (target.DescriptionPrivacy.CanAccess(pctx))
|
||||||
eb.Image(new Embed.EmbedImage(target.BannerImage));
|
eb.Image(new Embed.EmbedImage(target.BannerImage));
|
||||||
|
|
@ -369,7 +369,7 @@ public class EmbedService
|
||||||
.Build();
|
.Build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Embed> CreateMessageInfoEmbed(FullMessage msg, bool showContent)
|
public async Task<Embed> CreateMessageInfoEmbed(FullMessage msg, bool showContent, SystemConfig? ccfg = null)
|
||||||
{
|
{
|
||||||
var channel = await _cache.GetOrFetchChannel(_rest, msg.Message.Channel);
|
var channel = await _cache.GetOrFetchChannel(_rest, msg.Message.Channel);
|
||||||
var ctx = LookupContext.ByNonOwner;
|
var ctx = LookupContext.ByNonOwner;
|
||||||
|
|
@ -424,12 +424,12 @@ public class EmbedService
|
||||||
.Field(new Embed.Field("System",
|
.Field(new Embed.Field("System",
|
||||||
msg.System == null
|
msg.System == null
|
||||||
? "*(deleted or unknown system)*"
|
? "*(deleted or unknown system)*"
|
||||||
: msg.System.NameFor(ctx) != null ? $"{msg.System.NameFor(ctx)} (`{msg.System.Hid}`)" : $"`{msg.System.Hid}`"
|
: msg.System.NameFor(ctx) != null ? $"{msg.System.NameFor(ctx)} (`{msg.System.DisplayHid(ccfg)}`)" : $"`{msg.System.DisplayHid(ccfg)}`"
|
||||||
, true))
|
, true))
|
||||||
.Field(new Embed.Field("Member",
|
.Field(new Embed.Field("Member",
|
||||||
msg.Member == null
|
msg.Member == null
|
||||||
? "*(deleted member)*"
|
? "*(deleted member)*"
|
||||||
: $"{msg.Member.NameFor(ctx)} (`{msg.Member.Hid}`)"
|
: $"{msg.Member.NameFor(ctx)} (`{msg.Member.DisplayHid(ccfg)}`)"
|
||||||
, true))
|
, true))
|
||||||
.Field(new Embed.Field("Sent by", userStr, true))
|
.Field(new Embed.Field("Sent by", userStr, true))
|
||||||
.Timestamp(DiscordUtils.SnowflakeToInstant(msg.Message.Mid).ToDateTimeOffset().ToString("O"))
|
.Timestamp(DiscordUtils.SnowflakeToInstant(msg.Message.Mid).ToDateTimeOffset().ToString("O"))
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,11 @@ public class InteractionContext
|
||||||
private readonly ILifetimeScope _provider;
|
private readonly ILifetimeScope _provider;
|
||||||
private readonly IMetrics _metrics;
|
private readonly IMetrics _metrics;
|
||||||
|
|
||||||
public InteractionContext(ILifetimeScope provider, InteractionCreateEvent evt, PKSystem system)
|
public InteractionContext(ILifetimeScope provider, InteractionCreateEvent evt, PKSystem system, SystemConfig config)
|
||||||
{
|
{
|
||||||
Event = evt;
|
Event = evt;
|
||||||
System = system;
|
System = system;
|
||||||
|
Config = config;
|
||||||
Cache = provider.Resolve<IDiscordCache>();
|
Cache = provider.Resolve<IDiscordCache>();
|
||||||
Rest = provider.Resolve<DiscordApiClient>();
|
Rest = provider.Resolve<DiscordApiClient>();
|
||||||
Repository = provider.Resolve<ModelRepository>();
|
Repository = provider.Resolve<ModelRepository>();
|
||||||
|
|
@ -31,6 +32,7 @@ public class InteractionContext
|
||||||
internal readonly DiscordApiClient Rest;
|
internal readonly DiscordApiClient Rest;
|
||||||
internal readonly ModelRepository Repository;
|
internal readonly ModelRepository Repository;
|
||||||
public readonly PKSystem System;
|
public readonly PKSystem System;
|
||||||
|
public readonly SystemConfig Config;
|
||||||
|
|
||||||
public InteractionCreateEvent Event { get; }
|
public InteractionCreateEvent Event { get; }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,14 @@ public static class ModelUtils
|
||||||
public static string DisplayName(this PKMember member) =>
|
public static string DisplayName(this PKMember member) =>
|
||||||
member.DisplayName ?? member.Name;
|
member.DisplayName ?? member.Name;
|
||||||
|
|
||||||
public static string Reference(this PKMember member, Context ctx) => EntityReference(member.Hid, member.NameFor(ctx));
|
public static string Reference(this PKMember member, Context ctx) => EntityReference(member.DisplayHid(ctx.Config), member.NameFor(ctx));
|
||||||
public static string Reference(this PKGroup group, Context ctx) => EntityReference(group.Hid, group.NameFor(ctx));
|
public static string Reference(this PKGroup group, Context ctx) => EntityReference(group.DisplayHid(ctx.Config), group.NameFor(ctx));
|
||||||
|
|
||||||
|
|
||||||
|
public static string DisplayHid(this PKSystem system, SystemConfig? cfg = null) => HidTransform(system.Hid, cfg);
|
||||||
|
public static string DisplayHid(this PKGroup group, SystemConfig? cfg = null) => HidTransform(group.Hid, cfg);
|
||||||
|
public static string DisplayHid(this PKMember member, SystemConfig? cfg = null) => HidTransform(member.Hid, cfg);
|
||||||
|
private static string HidTransform(string hid, SystemConfig? cfg = null) => HidUtils.HidTransform(hid, cfg != null && cfg.HidDisplaySplit);
|
||||||
|
|
||||||
private static string EntityReference(string hid, string name)
|
private static string EntityReference(string hid, string name)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -122,13 +122,13 @@ begin
|
||||||
end
|
end
|
||||||
$$ language plpgsql;
|
$$ language plpgsql;
|
||||||
|
|
||||||
create function generate_hid() returns char(5) as $$
|
create function generate_hid() returns char(6) as $$
|
||||||
select string_agg(substr('abcdefghijklmnopqrstuvwxyz', ceil(random() * 26)::integer, 1), '') from generate_series(1, 5)
|
select string_agg(substr('abcefghjknoprstuvwxyz', ceil(random() * 21)::integer, 1), '') from generate_series(1, 6)
|
||||||
$$ language sql volatile;
|
$$ language sql volatile;
|
||||||
|
|
||||||
|
|
||||||
create function find_free_system_hid() returns char(5) as $$
|
create function find_free_system_hid() returns char(6) as $$
|
||||||
declare new_hid char(5);
|
declare new_hid char(6);
|
||||||
begin
|
begin
|
||||||
loop
|
loop
|
||||||
new_hid := generate_hid();
|
new_hid := generate_hid();
|
||||||
|
|
@ -138,8 +138,8 @@ end
|
||||||
$$ language plpgsql volatile;
|
$$ language plpgsql volatile;
|
||||||
|
|
||||||
|
|
||||||
create function find_free_member_hid() returns char(5) as $$
|
create function find_free_member_hid() returns char(6) as $$
|
||||||
declare new_hid char(5);
|
declare new_hid char(6);
|
||||||
begin
|
begin
|
||||||
loop
|
loop
|
||||||
new_hid := generate_hid();
|
new_hid := generate_hid();
|
||||||
|
|
@ -148,8 +148,9 @@ begin
|
||||||
end
|
end
|
||||||
$$ language plpgsql volatile;
|
$$ language plpgsql volatile;
|
||||||
|
|
||||||
create function find_free_group_hid() returns char(5) as $$
|
|
||||||
declare new_hid char(5);
|
create function find_free_group_hid() returns char(6) as $$
|
||||||
|
declare new_hid char(6);
|
||||||
begin
|
begin
|
||||||
loop
|
loop
|
||||||
new_hid := generate_hid();
|
new_hid := generate_hid();
|
||||||
|
|
|
||||||
10
PluralKit.Core/Database/Migrations/42.sql
Normal file
10
PluralKit.Core/Database/Migrations/42.sql
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
-- database version 42
|
||||||
|
-- move to 6 character HIDs, add HID display config setting
|
||||||
|
|
||||||
|
alter table systems alter column hid type char(6) using rpad(hid, 6, ' ');
|
||||||
|
alter table members alter column hid type char(6) using rpad(hid, 6, ' ');
|
||||||
|
alter table groups alter column hid type char(6) using rpad(hid, 6, ' ');
|
||||||
|
|
||||||
|
alter table system_config add column hid_display_split bool default false;
|
||||||
|
|
||||||
|
update info set schema_version = 42;
|
||||||
|
|
@ -9,7 +9,7 @@ namespace PluralKit.Core;
|
||||||
internal class DatabaseMigrator
|
internal class DatabaseMigrator
|
||||||
{
|
{
|
||||||
private const string RootPath = "PluralKit.Core.Database"; // "resource path" root for SQL files
|
private const string RootPath = "PluralKit.Core.Database"; // "resource path" root for SQL files
|
||||||
private const int TargetSchemaVersion = 41;
|
private const int TargetSchemaVersion = 42;
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
public DatabaseMigrator(ILogger logger)
|
public DatabaseMigrator(ILogger logger)
|
||||||
|
|
|
||||||
35
PluralKit.Core/Database/Utils/HidUtils.cs
Normal file
35
PluralKit.Core/Database/Utils/HidUtils.cs
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace PluralKit.Core;
|
||||||
|
|
||||||
|
public static class HidUtils
|
||||||
|
{
|
||||||
|
private static readonly Regex _hidRegex = new(@"^[a-zA-Z]{5,6}$");
|
||||||
|
|
||||||
|
public static string? ParseHid(string input)
|
||||||
|
{
|
||||||
|
input = input.ToLower().Replace("-", null);
|
||||||
|
if (!_hidRegex.IsMatch(input))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool TryParseHid(this string input, out string hid)
|
||||||
|
{
|
||||||
|
hid = ParseHid(input);
|
||||||
|
return hid != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string HidTransform(string input, bool split = false)
|
||||||
|
{
|
||||||
|
if (split && input.Length > 5)
|
||||||
|
{
|
||||||
|
var len = (int)Math.Floor(input.Length / 2.0);
|
||||||
|
input = string.Concat(input.AsSpan(0, len), "-", input.AsSpan(len));
|
||||||
|
}
|
||||||
|
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -32,7 +32,13 @@ public readonly struct GroupId: INumericId<GroupId, int>
|
||||||
public class PKGroup
|
public class PKGroup
|
||||||
{
|
{
|
||||||
public GroupId Id { get; private set; }
|
public GroupId Id { get; private set; }
|
||||||
public string Hid { get; private set; } = null!;
|
private string _hid = null!;
|
||||||
|
public string Hid
|
||||||
|
{
|
||||||
|
private set => _hid = value.Trim();
|
||||||
|
get => _hid;
|
||||||
|
}
|
||||||
|
|
||||||
public Guid Uuid { get; private set; }
|
public Guid Uuid { get; private set; }
|
||||||
public SystemId System { get; private set; }
|
public SystemId System { get; private set; }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,13 @@ public class PKMember
|
||||||
// Dapper *can* figure out mapping to getter-only properties, but this doesn't work
|
// Dapper *can* figure out mapping to getter-only properties, but this doesn't work
|
||||||
// when trying to map to *subclasses* (eg. ListedMember). Adding private setters makes it work anyway.
|
// when trying to map to *subclasses* (eg. ListedMember). Adding private setters makes it work anyway.
|
||||||
public MemberId Id { get; private set; }
|
public MemberId Id { get; private set; }
|
||||||
public string Hid { get; private set; }
|
private string _hid = null!;
|
||||||
|
public string Hid
|
||||||
|
{
|
||||||
|
private set => _hid = value.Trim();
|
||||||
|
get => _hid;
|
||||||
|
}
|
||||||
|
|
||||||
public Guid Uuid { get; private set; }
|
public Guid Uuid { get; private set; }
|
||||||
public SystemId System { get; private set; }
|
public SystemId System { get; private set; }
|
||||||
public string Color { get; private set; }
|
public string Color { get; private set; }
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,13 @@ public readonly struct SystemId: INumericId<SystemId, int>
|
||||||
public class PKSystem
|
public class PKSystem
|
||||||
{
|
{
|
||||||
[Key] public SystemId Id { get; }
|
[Key] public SystemId Id { get; }
|
||||||
public string Hid { get; }
|
private string _hid = null!;
|
||||||
|
public string Hid
|
||||||
|
{
|
||||||
|
private set => _hid = value.Trim();
|
||||||
|
get => _hid;
|
||||||
|
}
|
||||||
|
|
||||||
public Guid Uuid { get; private set; }
|
public Guid Uuid { get; private set; }
|
||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
public string Description { get; }
|
public string Description { get; }
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ public class SystemConfigPatch: PatchObject
|
||||||
public Partial<string[]> DescriptionTemplates { get; set; }
|
public Partial<string[]> DescriptionTemplates { get; set; }
|
||||||
public Partial<bool> CaseSensitiveProxyTags { get; set; }
|
public Partial<bool> CaseSensitiveProxyTags { get; set; }
|
||||||
public Partial<bool> ProxyErrorMessageEnabled { get; set; }
|
public Partial<bool> ProxyErrorMessageEnabled { get; set; }
|
||||||
|
public Partial<bool> HidDisplaySplit { get; set; }
|
||||||
|
|
||||||
|
|
||||||
public override Query Apply(Query q) => q.ApplyPatch(wrapper => wrapper
|
public override Query Apply(Query q) => q.ApplyPatch(wrapper => wrapper
|
||||||
|
|
@ -33,6 +34,7 @@ public class SystemConfigPatch: PatchObject
|
||||||
.With("description_templates", DescriptionTemplates)
|
.With("description_templates", DescriptionTemplates)
|
||||||
.With("case_sensitive_proxy_tags", CaseSensitiveProxyTags)
|
.With("case_sensitive_proxy_tags", CaseSensitiveProxyTags)
|
||||||
.With("proxy_error_message_enabled", ProxyErrorMessageEnabled)
|
.With("proxy_error_message_enabled", ProxyErrorMessageEnabled)
|
||||||
|
.With("hid_display_split", HidDisplaySplit)
|
||||||
);
|
);
|
||||||
|
|
||||||
public new void AssertIsValid()
|
public new void AssertIsValid()
|
||||||
|
|
@ -88,6 +90,9 @@ public class SystemConfigPatch: PatchObject
|
||||||
if (ProxyErrorMessageEnabled.IsPresent)
|
if (ProxyErrorMessageEnabled.IsPresent)
|
||||||
o.Add("proxy_error_message_enabled", ProxyErrorMessageEnabled.Value);
|
o.Add("proxy_error_message_enabled", ProxyErrorMessageEnabled.Value);
|
||||||
|
|
||||||
|
if (HidDisplaySplit.IsPresent)
|
||||||
|
o.Add("hid_display_split", HidDisplaySplit.Value);
|
||||||
|
|
||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -119,6 +124,9 @@ public class SystemConfigPatch: PatchObject
|
||||||
if (o.ContainsKey("proxy_error_message_enabled"))
|
if (o.ContainsKey("proxy_error_message_enabled"))
|
||||||
patch.ProxyErrorMessageEnabled = o.Value<bool>("proxy_error_message_enabled");
|
patch.ProxyErrorMessageEnabled = o.Value<bool>("proxy_error_message_enabled");
|
||||||
|
|
||||||
|
if (o.ContainsKey("hid_display_split"))
|
||||||
|
patch.HidDisplaySplit = o.Value<bool>("hid_display_split");
|
||||||
|
|
||||||
return patch;
|
return patch;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -19,8 +19,9 @@ public class SystemConfig
|
||||||
|
|
||||||
public DateTimeZone Zone => DateTimeZoneProviders.Tzdb.GetZoneOrNull(UiTz);
|
public DateTimeZone Zone => DateTimeZoneProviders.Tzdb.GetZoneOrNull(UiTz);
|
||||||
|
|
||||||
public bool CaseSensitiveProxyTags { get; set; }
|
public bool CaseSensitiveProxyTags { get; }
|
||||||
public bool ProxyErrorMessageEnabled { get; }
|
public bool ProxyErrorMessageEnabled { get; }
|
||||||
|
public bool HidDisplaySplit { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class SystemConfigExt
|
public static class SystemConfigExt
|
||||||
|
|
@ -39,6 +40,7 @@ public static class SystemConfigExt
|
||||||
o.Add("group_limit", cfg.GroupLimitOverride ?? Limits.MaxGroupCount);
|
o.Add("group_limit", cfg.GroupLimitOverride ?? Limits.MaxGroupCount);
|
||||||
o.Add("case_sensitive_proxy_tags", cfg.CaseSensitiveProxyTags);
|
o.Add("case_sensitive_proxy_tags", cfg.CaseSensitiveProxyTags);
|
||||||
o.Add("proxy_error_message_enabled", cfg.ProxyErrorMessageEnabled);
|
o.Add("proxy_error_message_enabled", cfg.ProxyErrorMessageEnabled);
|
||||||
|
o.Add("hid_display_split", cfg.HidDisplaySplit);
|
||||||
|
|
||||||
o.Add("description_templates", JArray.FromObject(cfg.DescriptionTemplates));
|
o.Add("description_templates", JArray.FromObject(cfg.DescriptionTemplates));
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue