parent
96b03495a4
commit
393ee16c1b
@ -1,3 +1,4 @@
|
|||||||
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using PluralKit.Core;
|
using PluralKit.Core;
|
||||||
@ -10,13 +11,11 @@ namespace PluralKit.API.Controllers
|
|||||||
public class MemberController: ControllerBase
|
public class MemberController: ControllerBase
|
||||||
{
|
{
|
||||||
private IDataStore _data;
|
private IDataStore _data;
|
||||||
private DbConnectionFactory _conn;
|
|
||||||
private TokenAuthService _auth;
|
private TokenAuthService _auth;
|
||||||
|
|
||||||
public MemberController(IDataStore data, DbConnectionFactory conn, TokenAuthService auth)
|
public MemberController(IDataStore data, TokenAuthService auth)
|
||||||
{
|
{
|
||||||
_data = data;
|
_data = data;
|
||||||
_conn = conn;
|
|
||||||
_auth = auth;
|
_auth = auth;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,7 +35,7 @@ namespace PluralKit.API.Controllers
|
|||||||
var system = _auth.CurrentSystem;
|
var system = _auth.CurrentSystem;
|
||||||
|
|
||||||
if (newMember.Name == null)
|
if (newMember.Name == null)
|
||||||
return BadRequest("Member name cannot be null.");
|
return BadRequest("Member name cannot be null.");
|
||||||
|
|
||||||
// Enforce per-system member limit
|
// Enforce per-system member limit
|
||||||
var memberCount = await _data.GetSystemMemberCount(system);
|
var memberCount = await _data.GetSystemMemberCount(system);
|
||||||
@ -56,9 +55,7 @@ namespace PluralKit.API.Controllers
|
|||||||
// Sanity bounds checks
|
// Sanity bounds checks
|
||||||
if (newMember.AvatarUrl != null && newMember.AvatarUrl.Length > 1000)
|
if (newMember.AvatarUrl != null && newMember.AvatarUrl.Length > 1000)
|
||||||
return BadRequest();
|
return BadRequest();
|
||||||
if (newMember.Prefix != null && newMember.Prefix.Length > 1000)
|
if (newMember.ProxyTags?.Any(tag => tag.Prefix.Length > 1000 || tag.Suffix.Length > 1000) ?? false)
|
||||||
return BadRequest();
|
|
||||||
if (newMember.Suffix != null && newMember.Suffix.Length > 1000)
|
|
||||||
return BadRequest();
|
return BadRequest();
|
||||||
|
|
||||||
var member = await _data.CreateMember(system, newMember.Name);
|
var member = await _data.CreateMember(system, newMember.Name);
|
||||||
@ -70,8 +67,7 @@ namespace PluralKit.API.Controllers
|
|||||||
member.Birthday = newMember.Birthday;
|
member.Birthday = newMember.Birthday;
|
||||||
member.Pronouns = newMember.Pronouns;
|
member.Pronouns = newMember.Pronouns;
|
||||||
member.Description = newMember.Description;
|
member.Description = newMember.Description;
|
||||||
member.Prefix = newMember.Prefix;
|
member.ProxyTags = newMember.ProxyTags;
|
||||||
member.Suffix = newMember.Suffix;
|
|
||||||
await _data.SaveMember(member);
|
await _data.SaveMember(member);
|
||||||
|
|
||||||
return Ok(member);
|
return Ok(member);
|
||||||
@ -100,11 +96,7 @@ namespace PluralKit.API.Controllers
|
|||||||
return BadRequest($"Member descriptions too long ({newMember.Description.Length} > {Limits.MaxDescriptionLength}.");
|
return BadRequest($"Member descriptions too long ({newMember.Description.Length} > {Limits.MaxDescriptionLength}.");
|
||||||
|
|
||||||
// Sanity bounds checks
|
// Sanity bounds checks
|
||||||
if (newMember.AvatarUrl != null && newMember.AvatarUrl.Length > 1000)
|
if (newMember.ProxyTags?.Any(tag => tag.Prefix.Length > 1000 || tag.Suffix.Length > 1000) ?? false)
|
||||||
return BadRequest();
|
|
||||||
if (newMember.Prefix != null && newMember.Prefix.Length > 1000)
|
|
||||||
return BadRequest();
|
|
||||||
if (newMember.Suffix != null && newMember.Suffix.Length > 1000)
|
|
||||||
return BadRequest();
|
return BadRequest();
|
||||||
|
|
||||||
member.Name = newMember.Name;
|
member.Name = newMember.Name;
|
||||||
@ -114,8 +106,7 @@ namespace PluralKit.API.Controllers
|
|||||||
member.Birthday = newMember.Birthday;
|
member.Birthday = newMember.Birthday;
|
||||||
member.Pronouns = newMember.Pronouns;
|
member.Pronouns = newMember.Pronouns;
|
||||||
member.Description = newMember.Description;
|
member.Description = newMember.Description;
|
||||||
member.Prefix = newMember.Prefix;
|
member.ProxyTags = newMember.ProxyTags;
|
||||||
member.Suffix = newMember.Suffix;
|
|
||||||
await _data.SaveMember(member);
|
await _data.SaveMember(member);
|
||||||
|
|
||||||
return Ok(member);
|
return Ok(member);
|
||||||
|
@ -28,7 +28,7 @@ namespace PluralKit.Bot.Commands
|
|||||||
public static Command MemberPronouns = new Command("member pronouns", "member <member> pronouns [pronouns]", "uwu");
|
public static Command MemberPronouns = new Command("member pronouns", "member <member> pronouns [pronouns]", "uwu");
|
||||||
public static Command MemberColor = new Command("member color", "member <member> color [color]", "uwu");
|
public static Command MemberColor = new Command("member color", "member <member> color [color]", "uwu");
|
||||||
public static Command MemberBirthday = new Command("member birthday", "member <member> birthday [birthday]", "uwu");
|
public static Command MemberBirthday = new Command("member birthday", "member <member> birthday [birthday]", "uwu");
|
||||||
public static Command MemberProxy = new Command("member proxy", "member <member> proxy [example proxy]", "uwu");
|
public static Command MemberProxy = new Command("member proxy", "member <member> proxy [add|remove] [example proxy]", "uwu");
|
||||||
public static Command MemberDelete = new Command("member delete", "member <member> delete", "uwu");
|
public static Command MemberDelete = new Command("member delete", "member <member> delete", "uwu");
|
||||||
public static Command MemberAvatar = new Command("member avatar", "member <member> avatar [url|@mention]", "uwu");
|
public static Command MemberAvatar = new Command("member avatar", "member <member> avatar [url|@mention]", "uwu");
|
||||||
public static Command MemberDisplayName = new Command("member displayname", "member <member> displayname [display name]", "uwu");
|
public static Command MemberDisplayName = new Command("member displayname", "member <member> displayname [display name]", "uwu");
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -153,32 +154,74 @@ namespace PluralKit.Bot.Commands
|
|||||||
{
|
{
|
||||||
if (ctx.System == null) throw Errors.NoSystemError;
|
if (ctx.System == null) throw Errors.NoSystemError;
|
||||||
if (target.System != ctx.System.Id) throw Errors.NotOwnMemberError;
|
if (target.System != ctx.System.Id) throw Errors.NotOwnMemberError;
|
||||||
|
|
||||||
// Handling the clear case in an if here to keep the body dedented
|
ProxyTag ParseProxyTags(string exampleProxy)
|
||||||
var exampleProxy = ctx.RemainderOrNull();
|
|
||||||
if (exampleProxy == null)
|
|
||||||
{
|
{
|
||||||
// Just reset and send OK message
|
// // Make sure there's one and only one instance of "text" in the example proxy given
|
||||||
target.Prefix = null;
|
var prefixAndSuffix = exampleProxy.Split("text");
|
||||||
target.Suffix = null;
|
if (prefixAndSuffix.Length < 2) throw Errors.ProxyMustHaveText;
|
||||||
await _data.SaveMember(target);
|
if (prefixAndSuffix.Length > 2) throw Errors.ProxyMultipleText;
|
||||||
await ctx.Reply($"{Emojis.Success} Member proxy tags cleared.");
|
return new ProxyTag(prefixAndSuffix[0], prefixAndSuffix[1]);
|
||||||
|
|
||||||
await _proxyCache.InvalidateResultsForSystem(ctx.System);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure there's one and only one instance of "text" in the example proxy given
|
// "Sub"command: no arguments clearing
|
||||||
var prefixAndSuffix = exampleProxy.Split("text");
|
if (!ctx.HasNext())
|
||||||
if (prefixAndSuffix.Length < 2) throw Errors.ProxyMustHaveText;
|
{
|
||||||
if (prefixAndSuffix.Length > 2) throw Errors.ProxyMultipleText;
|
// If we already have multiple tags, this would clear everything, so prompt that
|
||||||
|
var msg = await ctx.Reply(
|
||||||
|
$"{Emojis.Warn} You already have multiple proxy tags set: {target.ProxyTagsString()}\nDo you want to clear them all?");
|
||||||
|
if (!await ctx.PromptYesNo(msg))
|
||||||
|
throw Errors.GenericCancelled();
|
||||||
|
|
||||||
|
target.ProxyTags = new ProxyTag[] { };
|
||||||
|
|
||||||
|
await _data.SaveMember(target);
|
||||||
|
await ctx.Reply($"{Emojis.Success} Proxy tags cleared.");
|
||||||
|
}
|
||||||
|
// Subcommand: "add"
|
||||||
|
else if (ctx.Match("add"))
|
||||||
|
{
|
||||||
|
var tagToAdd = ParseProxyTags(ctx.RemainderOrNull());
|
||||||
|
if (target.ProxyTags.Contains(tagToAdd))
|
||||||
|
throw Errors.ProxyTagAlreadyExists(tagToAdd, target);
|
||||||
|
|
||||||
|
// It's not guaranteed the list's mutable, so we force it to be
|
||||||
|
target.ProxyTags = target.ProxyTags.ToList();
|
||||||
|
target.ProxyTags.Add(tagToAdd);
|
||||||
|
|
||||||
|
await _data.SaveMember(target);
|
||||||
|
await ctx.Reply($"{Emojis.Success} Added proxy tags `{tagToAdd.ProxyString.SanitizeMentions()}`.");
|
||||||
|
}
|
||||||
|
// Subcommand: "remove"
|
||||||
|
else if (ctx.Match("remove"))
|
||||||
|
{
|
||||||
|
var tagToRemove = ParseProxyTags(ctx.RemainderOrNull());
|
||||||
|
if (!target.ProxyTags.Contains(tagToRemove))
|
||||||
|
throw Errors.ProxyTagDoesNotExist(tagToRemove, target);
|
||||||
|
|
||||||
|
// It's not guaranteed the list's mutable, so we force it to be
|
||||||
|
target.ProxyTags = target.ProxyTags.ToList();
|
||||||
|
target.ProxyTags.Remove(tagToRemove);
|
||||||
|
|
||||||
|
await _data.SaveMember(target);
|
||||||
|
await ctx.Reply($"{Emojis.Success} Removed proxy tags `{tagToRemove.ProxyString.SanitizeMentions()}`.");
|
||||||
|
}
|
||||||
|
// Subcommand: bare proxy tag given
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var requestedTag = ParseProxyTags(ctx.RemainderOrNull());
|
||||||
|
|
||||||
|
// This is mostly a legacy command, so it's gonna error out if there's
|
||||||
|
// already more than one proxy tag.
|
||||||
|
if (target.ProxyTags.Count > 1)
|
||||||
|
throw Errors.LegacyAlreadyHasProxyTag(requestedTag, target);
|
||||||
|
|
||||||
|
target.ProxyTags = new[] {requestedTag};
|
||||||
|
|
||||||
|
await _data.SaveMember(target);
|
||||||
|
await ctx.Reply($"{Emojis.Success} Member proxy tags set to `{requestedTag.ProxyString.SanitizeMentions()}`.");
|
||||||
|
}
|
||||||
|
|
||||||
// If the prefix/suffix is empty, use "null" instead (for DB)
|
|
||||||
target.Prefix = prefixAndSuffix[0].Length > 0 ? prefixAndSuffix[0] : null;
|
|
||||||
target.Suffix = prefixAndSuffix[1].Length > 0 ? prefixAndSuffix[1] : null;
|
|
||||||
await _data.SaveMember(target);
|
|
||||||
await ctx.Reply($"{Emojis.Success} Member proxy tags changed to `{target.ProxyString.SanitizeMentions()}`. Try proxying now!");
|
|
||||||
|
|
||||||
await _proxyCache.InvalidateResultsForSystem(ctx.System);
|
await _proxyCache.InvalidateResultsForSystem(ctx.System);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,7 +134,7 @@ namespace PluralKit.Bot.Commands
|
|||||||
25,
|
25,
|
||||||
embedTitle,
|
embedTitle,
|
||||||
(eb, ms) => eb.Description = string.Join("\n", ms.Select((m) => {
|
(eb, ms) => eb.Description = string.Join("\n", ms.Select((m) => {
|
||||||
if (m.HasProxyTags) return $"[`{m.Hid}`] **{m.Name.SanitizeMentions()}** *({m.ProxyString.SanitizeMentions()})*";
|
if (m.HasProxyTags) return $"[`{m.Hid}`] **{m.Name.SanitizeMentions()}** *({m.ProxyTagsString().SanitizeMentions()})*";
|
||||||
return $"[`{m.Hid}`] **{m.Name.SanitizeMentions()}**";
|
return $"[`{m.Hid}`] **{m.Name.SanitizeMentions()}**";
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
@ -154,7 +154,7 @@ namespace PluralKit.Bot.Commands
|
|||||||
var profile = $"**ID**: {m.Hid}";
|
var profile = $"**ID**: {m.Hid}";
|
||||||
if (m.Pronouns != null) profile += $"\n**Pronouns**: {m.Pronouns}";
|
if (m.Pronouns != null) profile += $"\n**Pronouns**: {m.Pronouns}";
|
||||||
if (m.Birthday != null) profile += $"\n**Birthdate**: {m.BirthdayString}";
|
if (m.Birthday != null) profile += $"\n**Birthdate**: {m.BirthdayString}";
|
||||||
if (m.Prefix != null || m.Suffix != null) profile += $"\n**Proxy tags**: {m.ProxyString}";
|
if (m.ProxyTags.Count > 0) profile += $"\n**Proxy tags:** {m.ProxyTagsString()}";
|
||||||
if (m.Description != null) profile += $"\n\n{m.Description}";
|
if (m.Description != null) profile += $"\n\n{m.Description}";
|
||||||
eb.AddField(m.Name, profile.Truncate(1024));
|
eb.AddField(m.Name, profile.Truncate(1024));
|
||||||
}
|
}
|
||||||
|
@ -80,5 +80,11 @@ namespace PluralKit.Bot {
|
|||||||
$"Display name too long ({displayName.Length} > {maxLength} characters). Use a shorter display name, or shorten your system tag.");
|
$"Display name too long ({displayName.Length} > {maxLength} characters). Use a shorter display name, or shorten your system tag.");
|
||||||
public static PKError ProxyNameTooShort(string name) => new PKError($"The webhook's name, `{name.SanitizeMentions()}`, is shorter than two characters, and thus cannot be proxied. Please change the member name or use a longer system tag.");
|
public static PKError ProxyNameTooShort(string name) => new PKError($"The webhook's name, `{name.SanitizeMentions()}`, is shorter than two characters, and thus cannot be proxied. Please change the member name or use a longer system tag.");
|
||||||
public static PKError ProxyNameTooLong(string name) => new PKError($"The webhook's name, {name.SanitizeMentions()}, is too long ({name.Length} > {Limits.MaxProxyNameLength} characters), and thus cannot be proxied. Please change the member name or use a shorter system tag.");
|
public static PKError ProxyNameTooLong(string name) => new PKError($"The webhook's name, {name.SanitizeMentions()}, is too long ({name.Length} > {Limits.MaxProxyNameLength} characters), and thus cannot be proxied. Please change the member name or use a shorter system tag.");
|
||||||
|
|
||||||
|
public static PKError ProxyTagAlreadyExists(ProxyTag tagToAdd, PKMember member) => new PKError($"That member already has the proxy tag `{tagToAdd.ProxyString.SanitizeMentions()}`. The member currently has these tags: {member.ProxyTagsString().SanitizeMentions()}");
|
||||||
|
public static PKError ProxyTagDoesNotExist(ProxyTag tagToRemove, PKMember member) => new PKError($"That member does not have the proxy tag `{tagToRemove.ProxyString.SanitizeMentions()}`. The member currently has these tags: {member.ProxyTagsString().SanitizeMentions()}");
|
||||||
|
public static PKError LegacyAlreadyHasProxyTag(ProxyTag requested, PKMember member) => new PKError($"This member already has more than one proxy tag set: {member.ProxyTagsString().SanitizeMentions()}\nConsider using the `pk;member {member.Hid} proxy add {requested.ProxyString.SanitizeMentions()}` command instead.");
|
||||||
|
|
||||||
|
public static PKError GenericCancelled() => new PKError("Operation cancelled.");
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -82,6 +82,8 @@ namespace PluralKit.Bot {
|
|||||||
|
|
||||||
var messageCount = await _data.GetMemberMessageCount(member);
|
var messageCount = await _data.GetMemberMessageCount(member);
|
||||||
|
|
||||||
|
var proxyTagsStr = string.Join('\n', member.ProxyTags.Select(t => $"`{t.ProxyString}`"));
|
||||||
|
|
||||||
var eb = new EmbedBuilder()
|
var eb = new EmbedBuilder()
|
||||||
// TODO: add URL of website when that's up
|
// TODO: add URL of website when that's up
|
||||||
.WithAuthor(name, member.AvatarUrl)
|
.WithAuthor(name, member.AvatarUrl)
|
||||||
@ -94,7 +96,7 @@ namespace PluralKit.Bot {
|
|||||||
if (member.Birthday != null) eb.AddField("Birthdate", member.BirthdayString, true);
|
if (member.Birthday != null) eb.AddField("Birthdate", member.BirthdayString, true);
|
||||||
if (member.Pronouns != null) eb.AddField("Pronouns", member.Pronouns, true);
|
if (member.Pronouns != null) eb.AddField("Pronouns", member.Pronouns, true);
|
||||||
if (messageCount > 0) eb.AddField("Message Count", messageCount, true);
|
if (messageCount > 0) eb.AddField("Message Count", messageCount, true);
|
||||||
if (member.HasProxyTags) eb.AddField("Proxy Tags", $"{member.Prefix.EscapeMarkdown()}text{member.Suffix.EscapeMarkdown()}", true);
|
if (member.HasProxyTags) eb.AddField("Proxy Tags", string.Join('\n', proxyTagsStr), true);
|
||||||
if (member.Color != null) eb.AddField("Color", $"#{member.Color}", true);
|
if (member.Color != null) eb.AddField("Color", $"#{member.Color}", true);
|
||||||
if (member.Description != null) eb.AddField("Description", member.Description, false);
|
if (member.Description != null) eb.AddField("Description", member.Description, false);
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ namespace PluralKit.Bot
|
|||||||
_httpClient = new HttpClient();
|
_httpClient = new HttpClient();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ProxyMatch GetProxyTagMatch(string message, IEnumerable<ProxyCacheService.ProxyDatabaseResult> potentials)
|
private ProxyMatch GetProxyTagMatch(string message, IEnumerable<ProxyCacheService.ProxyDatabaseResult> potentialMembers)
|
||||||
{
|
{
|
||||||
// If the message starts with a @mention, and then proceeds to have proxy tags,
|
// If the message starts with a @mention, and then proceeds to have proxy tags,
|
||||||
// extract the mention and place it inside the inner message
|
// extract the mention and place it inside the inner message
|
||||||
@ -57,19 +57,19 @@ namespace PluralKit.Bot
|
|||||||
message = message.Substring(matchStartPosition);
|
message = message.Substring(matchStartPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort by specificity (ProxyString length desc = prefix+suffix length desc = inner message asc = more specific proxy first!)
|
// Flatten and sort by specificity (ProxyString length desc = prefix+suffix length desc = inner message asc = more specific proxy first!)
|
||||||
var ordered = potentials.OrderByDescending(p => p.Member.ProxyString.Length);
|
var ordered = potentialMembers.SelectMany(m => m.Member.ProxyTags.Select(tag => (tag, m))).OrderByDescending(p => p.Item1.ProxyString);
|
||||||
foreach (var potential in ordered)
|
foreach (var (tag, match) in ordered)
|
||||||
{
|
{
|
||||||
if (potential.Member.Prefix == null && potential.Member.Suffix == null) continue;
|
if (tag.Prefix == null && tag.Suffix == null) continue;
|
||||||
|
|
||||||
var prefix = potential.Member.Prefix ?? "";
|
var prefix = tag.Prefix ?? "";
|
||||||
var suffix = potential.Member.Suffix ?? "";
|
var suffix = tag.Suffix ?? "";
|
||||||
|
|
||||||
if (message.Length >= prefix.Length + suffix.Length && message.StartsWith(prefix) && message.EndsWith(suffix)) {
|
if (message.Length >= prefix.Length + suffix.Length && message.StartsWith(prefix) && message.EndsWith(suffix)) {
|
||||||
var inner = message.Substring(prefix.Length, message.Length - prefix.Length - suffix.Length);
|
var inner = message.Substring(prefix.Length, message.Length - prefix.Length - suffix.Length);
|
||||||
if (leadingMention != null) inner = $"{leadingMention} {inner}";
|
if (leadingMention != null) inner = $"{leadingMention} {inner}";
|
||||||
return new ProxyMatch { Member = potential.Member, System = potential.System, InnerText = inner };
|
return new ProxyMatch { Member = match.Member, System = match.System, InnerText = inner };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,6 +100,8 @@ namespace PluralKit.Bot
|
|||||||
if (input != null) return pattern.Replace(input, @"\$&");
|
if (input != null) return pattern.Replace(input, @"\$&");
|
||||||
else return input;
|
else return input;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string ProxyTagsString(this PKMember member) => string.Join(", ", member.ProxyTags.Select(t => $"`{t.ProxyString.EscapeMarkdown()}`"));
|
||||||
|
|
||||||
public static async Task<ChannelPermissions> PermissionsIn(this IChannel channel)
|
public static async Task<ChannelPermissions> PermissionsIn(this IChannel channel)
|
||||||
{
|
{
|
||||||
|
@ -37,8 +37,7 @@ namespace PluralKit.Bot
|
|||||||
Pronouns = m.Pronouns,
|
Pronouns = m.Pronouns,
|
||||||
Color = m.Color,
|
Color = m.Color,
|
||||||
AvatarUrl = m.AvatarUrl,
|
AvatarUrl = m.AvatarUrl,
|
||||||
Prefix = m.Prefix,
|
ProxyTags = m.ProxyTags,
|
||||||
Suffix = m.Suffix,
|
|
||||||
Created = Formats.TimestampExportFormat.Format(m.Created),
|
Created = Formats.TimestampExportFormat.Format(m.Created),
|
||||||
MessageCount = messageCounts.Where(x => x.Member == m.Id).Select(x => x.MessageCount).FirstOrDefault()
|
MessageCount = messageCounts.Where(x => x.Member == m.Id).Select(x => x.MessageCount).FirstOrDefault()
|
||||||
}));
|
}));
|
||||||
@ -150,8 +149,7 @@ namespace PluralKit.Bot
|
|||||||
if (dataMember.AvatarUrl != null) member.AvatarUrl = dataMember.AvatarUrl;
|
if (dataMember.AvatarUrl != null) member.AvatarUrl = dataMember.AvatarUrl;
|
||||||
if (dataMember.Prefix != null || dataMember.Suffix != null)
|
if (dataMember.Prefix != null || dataMember.Suffix != null)
|
||||||
{
|
{
|
||||||
member.Prefix = dataMember.Prefix;
|
member.ProxyTags = new List<ProxyTag> { new ProxyTag(dataMember.Prefix, dataMember.Suffix) };
|
||||||
member.Suffix = dataMember.Suffix;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dataMember.Birthday != null)
|
if (dataMember.Birthday != null)
|
||||||
@ -223,8 +221,14 @@ namespace PluralKit.Bot
|
|||||||
[JsonProperty("pronouns")] public string Pronouns;
|
[JsonProperty("pronouns")] public string Pronouns;
|
||||||
[JsonProperty("color")] public string Color;
|
[JsonProperty("color")] public string Color;
|
||||||
[JsonProperty("avatar_url")] public string AvatarUrl;
|
[JsonProperty("avatar_url")] public string AvatarUrl;
|
||||||
[JsonProperty("prefix")] public string Prefix;
|
|
||||||
[JsonProperty("suffix")] public string Suffix;
|
// For legacy single-tag imports
|
||||||
|
[JsonProperty("prefix")] [JsonIgnore] public string Prefix;
|
||||||
|
[JsonProperty("suffix")] [JsonIgnore] public string Suffix;
|
||||||
|
|
||||||
|
// ^ is superseded by v
|
||||||
|
[JsonProperty("proxy_tags")] public ICollection<ProxyTag> ProxyTags;
|
||||||
|
|
||||||
[JsonProperty("message_count")] public int MessageCount;
|
[JsonProperty("message_count")] public int MessageCount;
|
||||||
[JsonProperty("created")] public string Created;
|
[JsonProperty("created")] public string Created;
|
||||||
|
|
||||||
|
@ -1,12 +1,42 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
using Dapper.Contrib.Extensions;
|
using Dapper.Contrib.Extensions;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using NodaTime;
|
using NodaTime;
|
||||||
using NodaTime.Text;
|
using NodaTime.Text;
|
||||||
|
|
||||||
using PluralKit.Core;
|
|
||||||
|
|
||||||
namespace PluralKit
|
namespace PluralKit
|
||||||
{
|
{
|
||||||
|
public struct ProxyTag
|
||||||
|
{
|
||||||
|
public ProxyTag(string prefix, string suffix)
|
||||||
|
{
|
||||||
|
// Normalize empty strings to null for DB
|
||||||
|
Prefix = prefix.Length == 0 ? null : prefix;
|
||||||
|
Suffix = suffix.Length == 0 ? null : suffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonProperty("prefix")] public string Prefix { get; set; }
|
||||||
|
[JsonProperty("suffix")] public string Suffix { get; set; }
|
||||||
|
|
||||||
|
[JsonIgnore] public string ProxyString => $"{Prefix ?? ""}text{Suffix ?? ""}";
|
||||||
|
|
||||||
|
public bool Equals(ProxyTag other) => Prefix == other.Prefix && Suffix == other.Suffix;
|
||||||
|
|
||||||
|
public override bool Equals(object obj) => obj is ProxyTag other && Equals(other);
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
unchecked
|
||||||
|
{
|
||||||
|
return ((Prefix != null ? Prefix.GetHashCode() : 0) * 397) ^
|
||||||
|
(Suffix != null ? Suffix.GetHashCode() : 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public class PKSystem
|
public class PKSystem
|
||||||
{
|
{
|
||||||
// Additions here should be mirrored in SystemStore::Save
|
// Additions here should be mirrored in SystemStore::Save
|
||||||
@ -35,9 +65,22 @@ namespace PluralKit
|
|||||||
[JsonProperty("birthday")] public LocalDate? Birthday { get; set; }
|
[JsonProperty("birthday")] public LocalDate? Birthday { get; set; }
|
||||||
[JsonProperty("pronouns")] public string Pronouns { get; set; }
|
[JsonProperty("pronouns")] public string Pronouns { get; set; }
|
||||||
[JsonProperty("description")] public string Description { get; set; }
|
[JsonProperty("description")] public string Description { get; set; }
|
||||||
[JsonProperty("prefix")] public string Prefix { get; set; }
|
[JsonProperty("proxy_tags")] public ICollection<ProxyTag> ProxyTags { get; set; }
|
||||||
[JsonProperty("suffix")] public string Suffix { get; set; }
|
|
||||||
[JsonProperty("created")] public Instant Created { get; set; }
|
[JsonProperty("created")] public Instant Created { get; set; }
|
||||||
|
|
||||||
|
// These are deprecated as fuck, and are kinda hacky
|
||||||
|
// Don't use, unless you're the API's serialization library
|
||||||
|
[JsonProperty("prefix")] [Obsolete("Use PKMember.ProxyTags")] public string Prefix
|
||||||
|
{
|
||||||
|
get => ProxyTags.FirstOrDefault().Prefix;
|
||||||
|
set => ProxyTags = new[] {new ProxyTag(Prefix, value)};
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonProperty("suffix")] [Obsolete("Use PKMember.ProxyTags")] public string Suffix
|
||||||
|
{
|
||||||
|
get => ProxyTags.FirstOrDefault().Prefix;
|
||||||
|
set => ProxyTags = new[] {new ProxyTag(Prefix, value)};
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns a formatted string representing the member's birthday, taking into account that a year of "0001" is hidden
|
/// Returns a formatted string representing the member's birthday, taking into account that a year of "0001" is hidden
|
||||||
[JsonIgnore] public string BirthdayString
|
[JsonIgnore] public string BirthdayString
|
||||||
@ -52,9 +95,7 @@ namespace PluralKit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonIgnore] public bool HasProxyTags => Prefix != null || Suffix != null;
|
[JsonIgnore] public bool HasProxyTags => ProxyTags.Count > 0;
|
||||||
[JsonIgnore] public string ProxyString => $"{Prefix ?? ""}text{Suffix ?? ""}";
|
|
||||||
|
|
||||||
public string ProxyName(string systemTag)
|
public string ProxyName(string systemTag)
|
||||||
{
|
{
|
||||||
if (systemTag == null) return DisplayName ?? Name;
|
if (systemTag == null) return DisplayName ?? Name;
|
||||||
|
@ -495,7 +495,7 @@ namespace PluralKit {
|
|||||||
|
|
||||||
public async Task SaveMember(PKMember member) {
|
public async Task SaveMember(PKMember member) {
|
||||||
using (var conn = await _conn.Obtain())
|
using (var conn = await _conn.Obtain())
|
||||||
await conn.ExecuteAsync("update members set name = @Name, display_name = @DisplayName, description = @Description, color = @Color, avatar_url = @AvatarUrl, birthday = @Birthday, pronouns = @Pronouns, prefix = @Prefix, suffix = @Suffix where id = @Id", member);
|
await conn.ExecuteAsync("update members set name = @Name, display_name = @DisplayName, description = @Description, color = @Color, avatar_url = @AvatarUrl, birthday = @Birthday, pronouns = @Pronouns, proxy_tags = @ProxyTags where id = @Id", member);
|
||||||
|
|
||||||
_logger.Information("Updated member {@Member}", member);
|
_logger.Information("Updated member {@Member}", member);
|
||||||
}
|
}
|
||||||
|
@ -310,6 +310,9 @@ namespace PluralKit
|
|||||||
// So we add a custom type handler that literally just passes the type through to Npgsql
|
// So we add a custom type handler that literally just passes the type through to Npgsql
|
||||||
SqlMapper.AddTypeHandler(new PassthroughTypeHandler<Instant>());
|
SqlMapper.AddTypeHandler(new PassthroughTypeHandler<Instant>());
|
||||||
SqlMapper.AddTypeHandler(new PassthroughTypeHandler<LocalDate>());
|
SqlMapper.AddTypeHandler(new PassthroughTypeHandler<LocalDate>());
|
||||||
|
|
||||||
|
// Add global type mapper for ProxyTag compound type in Postgres
|
||||||
|
NpgsqlConnection.GlobalTypeMapper.MapComposite<ProxyTag>("proxy_tag");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ILogger InitLogger(CoreConfig config, string component)
|
public static ILogger InitLogger(CoreConfig config, string component)
|
||||||
|
@ -1,3 +1,12 @@
|
|||||||
|
-- Create proxy_tag compound type if it doesn't exist
|
||||||
|
do $$ begin
|
||||||
|
create type proxy_tag as (
|
||||||
|
prefix text,
|
||||||
|
suffix text
|
||||||
|
);
|
||||||
|
exception when duplicate_object then null;
|
||||||
|
end $$;
|
||||||
|
|
||||||
create table if not exists systems
|
create table if not exists systems
|
||||||
(
|
(
|
||||||
id serial primary key,
|
id serial primary key,
|
||||||
@ -23,8 +32,7 @@ create table if not exists members
|
|||||||
birthday date,
|
birthday date,
|
||||||
pronouns text,
|
pronouns text,
|
||||||
description text,
|
description text,
|
||||||
prefix text,
|
proxy_tags proxy_tag[] not null default array[], -- Rationale on making this an array rather than a separate table - we never need to query them individually, only access them as part of a selected Member struct
|
||||||
suffix text,
|
|
||||||
created timestamp not null default (current_timestamp at time zone 'utc')
|
created timestamp not null default (current_timestamp at time zone 'utc')
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -255,6 +255,19 @@ You can now type a message enclosed in your proxy tags, and it'll be deleted by
|
|||||||
**NB:** If you want `<angle brackets>` as proxy tags, there is currently a bug where custom server emojis will (wrongly)
|
**NB:** If you want `<angle brackets>` as proxy tags, there is currently a bug where custom server emojis will (wrongly)
|
||||||
be interpreted as proxying with that member (see [issue #37](https://github.com/xSke/PluralKit/issues/37)). The current workaround is to use different proxy tags.
|
be interpreted as proxying with that member (see [issue #37](https://github.com/xSke/PluralKit/issues/37)). The current workaround is to use different proxy tags.
|
||||||
|
|
||||||
|
### Using multiple distinct proxy tag pairs
|
||||||
|
If you'd like to proxy a member in multiple ways (for example, a name or a nickname, uppercase and lowercase variants, etc), you can add multiple tag pairs.
|
||||||
|
When proxying, you may then use any of the tags to proxy for that specific member.
|
||||||
|
|
||||||
|
To add a proxy tag to a member, use the `pk;member proxy add` command:
|
||||||
|
pk;member John proxy add {text}
|
||||||
|
pk;member Craig proxy add C:text
|
||||||
|
|
||||||
|
To remove a proxy tag from a member, use the `pk;member proxy remove` command:
|
||||||
|
pk;member John proxy remove {text}
|
||||||
|
pk;member Craig proxy remove C:text
|
||||||
|
|
||||||
|
|
||||||
### Querying message information
|
### Querying message information
|
||||||
If you want information about a proxied message (eg. for moderation reasons), you can query the message for its sender account, system, member, etc.
|
If you want information about a proxied message (eg. for moderation reasons), you can query the message for its sender account, system, member, etc.
|
||||||
|
|
||||||
|
@ -32,7 +32,9 @@ Words in \<angle brackets> are *required parameters*. Words in [square brackets]
|
|||||||
- `pk;member <name> displayname <new display name>` - Changes the display name of a member.
|
- `pk;member <name> displayname <new display name>` - Changes the display name of a member.
|
||||||
- `pk;member <name> description [description]` - Changes the description of a member.
|
- `pk;member <name> description [description]` - Changes the description of a member.
|
||||||
- `pk;member <name> avatar [avatar url]` - Changes the avatar of a member.
|
- `pk;member <name> avatar [avatar url]` - Changes the avatar of a member.
|
||||||
- `pk;member <name> proxy [tags]` - Changes the proxy tags of a member.
|
- `pk;member <name> proxy [tags]` - Changes the proxy tags of a member. use below add/remove commands for members with multiple tag pairs.
|
||||||
|
- `pk;member <name> proxy add [tags]` - Adds a proxy tag pair to a member.
|
||||||
|
- `pk;member <name> proxy remove [tags]` - Removes a proxy tag from a member.
|
||||||
- `pk;member <name> pronouns [pronouns]` - Changes the pronouns of a member.
|
- `pk;member <name> pronouns [pronouns]` - Changes the pronouns of a member.
|
||||||
- `pk;member <name> color [color]` - Changes the color of a member.
|
- `pk;member <name> color [color]` - Changes the color of a member.
|
||||||
- `pk;member <name> birthdate [birthdate]` - Changes the birthday of a member.
|
- `pk;member <name> birthdate [birthdate]` - Changes the birthday of a member.
|
||||||
|
@ -44,10 +44,16 @@ The following three models (usually represented in JSON format) represent the va
|
|||||||
|color|color?|Yes|6-char hex (eg. `ff7000`), sans `#`.|
|
|color|color?|Yes|6-char hex (eg. `ff7000`), sans `#`.|
|
||||||
|avatar_url|url?|Yes|Not validated server-side.|
|
|avatar_url|url?|Yes|Not validated server-side.|
|
||||||
|birthday|date?|Yes|ISO-8601 (`YYYY-MM-DD`) format, year of `0001` means hidden year.|
|
|birthday|date?|Yes|ISO-8601 (`YYYY-MM-DD`) format, year of `0001` means hidden year.|
|
||||||
|prefix|string?|Yes||
|
|prefix|string?|Yes|Deprecated. Use `proxy_tags` instead.|
|
||||||
|suffix|string?|Yes||
|
|suffix|string?|Yes|Deprecated. Use `proxy_tags` instead.|
|
||||||
|
|proxy_tags|ProxyTag[]|Yes (entire array)|An array of ProxyTag (see below) objects, each representing a single prefix/suffix pair.|
|
||||||
|created|datetime|No||
|
|created|datetime|No||
|
||||||
|
|
||||||
|
#### ProxyTag object
|
||||||
|
|Key|Type|
|
||||||
|
|prefix|string?|
|
||||||
|
|suffix|string?|
|
||||||
|
|
||||||
### Switch model
|
### Switch model
|
||||||
|
|
||||||
|Key|Type|Notes|
|
|Key|Type|Notes|
|
||||||
|
Loading…
Reference in New Issue
Block a user