mirror of
https://github.com/PluralKit/PluralKit.git
synced 2026-02-16 18:50:13 +00:00
Compare commits
11 commits
9495b95afa
...
981546647a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
981546647a | ||
|
|
83f866384a | ||
|
|
0983179240 | ||
|
|
49ce00e675 | ||
|
|
83f2d33c3d | ||
|
|
14f11bd1e9 | ||
|
|
39179f8e3a | ||
|
|
24361d9d2b | ||
|
|
9c99a0bc02 | ||
|
|
ebf8a40369 | ||
|
|
8bca02032f |
13 changed files with 264 additions and 158 deletions
|
|
@ -43,23 +43,6 @@ public class ApplicationCommandProxiedMessage
|
|||
if (channel == null)
|
||||
showContent = false;
|
||||
|
||||
// var embeds = new List<Embed>();
|
||||
|
||||
// var guild = await _cache.GetGuild(ctx.GuildId);
|
||||
// if (msg.Member != null)
|
||||
// embeds.Add(await _embeds.CreateMemberEmbed(
|
||||
// msg.System,
|
||||
// msg.Member,
|
||||
// guild,
|
||||
// ctx.Config,
|
||||
// LookupContext.ByNonOwner,
|
||||
// DateTimeZone.Utc
|
||||
// ));
|
||||
|
||||
// embeds.Add(await _embeds.CreateMessageInfoEmbed(msg, showContent, ctx.Config));
|
||||
|
||||
// await ctx.Reply(embeds: embeds.ToArray());
|
||||
|
||||
var components = new List<MessageComponent>();
|
||||
var guild = await _cache.GetGuild(ctx.GuildId);
|
||||
if (msg.Member != null)
|
||||
|
|
@ -71,7 +54,10 @@ public class ApplicationCommandProxiedMessage
|
|||
LookupContext.ByNonOwner,
|
||||
DateTimeZone.Utc
|
||||
));
|
||||
|
||||
components.Add(new MessageComponent()
|
||||
{
|
||||
Type = ComponentType.Separator
|
||||
});
|
||||
components.AddRange(await _embeds.CreateMessageInfoMessageComponents(msg, showContent, ctx.Config));
|
||||
await ctx.Reply(components: components.ToArray());
|
||||
}
|
||||
|
|
@ -83,11 +69,7 @@ public class ApplicationCommandProxiedMessage
|
|||
if (msg == null)
|
||||
throw Errors.MessageNotFound(messageId);
|
||||
|
||||
var embeds = new List<Embed>();
|
||||
|
||||
embeds.Add(await _embeds.CreateCommandMessageInfoEmbed(msg, true));
|
||||
|
||||
await ctx.Reply(embeds: embeds.ToArray());
|
||||
await ctx.Reply(components: await _embeds.CreateCommandMessageInfoMessageComponents(msg, true));
|
||||
}
|
||||
|
||||
public async Task DeleteMessage(InteractionContext ctx)
|
||||
|
|
|
|||
|
|
@ -181,6 +181,8 @@ public partial class CommandTree
|
|||
await ctx.Execute<Admin>(Admin, a => a.SystemRecover(ctx));
|
||||
else if (ctx.Match("sd", "systemdelete"))
|
||||
await ctx.Execute<Admin>(Admin, a => a.SystemDelete(ctx));
|
||||
else if (ctx.Match("sendmsg", "sendmessage"))
|
||||
await ctx.Execute<Admin>(Admin, a => a.SendAdminMessage(ctx));
|
||||
else if (ctx.Match("al", "abuselog"))
|
||||
await HandleAdminAbuseLogCommand(ctx);
|
||||
else
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ using Myriad.Extensions;
|
|||
using Myriad.Cache;
|
||||
using Myriad.Rest;
|
||||
using Myriad.Types;
|
||||
using Myriad.Rest.Types.Requests;
|
||||
using Myriad.Rest.Exceptions;
|
||||
|
||||
using PluralKit.Core;
|
||||
|
||||
|
|
@ -19,12 +21,14 @@ public class Admin
|
|||
private readonly BotConfig _botConfig;
|
||||
private readonly DiscordApiClient _rest;
|
||||
private readonly IDiscordCache _cache;
|
||||
private readonly PrivateChannelService _dmCache;
|
||||
|
||||
public Admin(BotConfig botConfig, DiscordApiClient rest, IDiscordCache cache)
|
||||
public Admin(BotConfig botConfig, DiscordApiClient rest, IDiscordCache cache, PrivateChannelService dmCache)
|
||||
{
|
||||
_botConfig = botConfig;
|
||||
_rest = rest;
|
||||
_cache = cache;
|
||||
_dmCache = dmCache;
|
||||
}
|
||||
|
||||
private Task<(ulong Id, User? User)[]> GetUsers(IEnumerable<ulong> ids)
|
||||
|
|
@ -496,4 +500,34 @@ public class Admin
|
|||
await ctx.Repository.DeleteAbuseLog(abuseLog.Id);
|
||||
await ctx.Reply($"{Emojis.Success} Successfully deleted abuse log entry.");
|
||||
}
|
||||
|
||||
public async Task SendAdminMessage(Context ctx)
|
||||
{
|
||||
ctx.AssertBotAdmin();
|
||||
|
||||
var account = await ctx.MatchUser();
|
||||
if (account == null)
|
||||
throw new PKError("You must pass an account to send an admin message to (either ID or @mention).");
|
||||
if (!ctx.HasNext())
|
||||
throw new PKError("You must provide a message to send.");
|
||||
|
||||
var content = ctx.RemainderOrNull(false).NormalizeLineEndSpacing();
|
||||
var messageContent = $"## [Admin Message]\n\n{content}\n\nWe cannot read replies sent to this DM. If you wish to contact the staff team, please join the support server (<https://discord.gg/PczBt78>) or send us an email at <legal@pluralkit.me>.";
|
||||
|
||||
try
|
||||
{
|
||||
var dm = await _dmCache.GetOrCreateDmChannel(account.Id);
|
||||
var msg = await ctx.Rest.CreateMessage(dm,
|
||||
new MessageRequest { Content = messageContent }
|
||||
);
|
||||
}
|
||||
catch (ForbiddenException)
|
||||
{
|
||||
await ctx.Reply(
|
||||
$"{Emojis.Error} Error while sending DM.");
|
||||
return;
|
||||
}
|
||||
|
||||
await ctx.Reply($"{Emojis.Success} Successfully sent message.");
|
||||
}
|
||||
}
|
||||
|
|
@ -601,6 +601,13 @@ public class Config
|
|||
|
||||
public async Task FronterListFormat(Context ctx)
|
||||
{
|
||||
if (!ctx.HasNext())
|
||||
{
|
||||
var msg = $"Format of the fronter list is currently **{ctx.Config.FronterListFormat}**.";
|
||||
await ctx.Reply(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
var badInputError = "Valid list format settings are `short` or `full`.";
|
||||
if (ctx.Match("full", "f"))
|
||||
{
|
||||
|
|
@ -613,8 +620,6 @@ public class Config
|
|||
await ctx.Reply($"Fronter lists are now formatted as `short`");
|
||||
}
|
||||
else throw new PKError(badInputError);
|
||||
|
||||
|
||||
}
|
||||
|
||||
public async Task ProxySwitch(Context ctx)
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ public class GroupMember
|
|||
var opts = ctx.ParseListOptions(ctx.DirectLookupContextFor(target.System), ctx.LookupContextFor(target.System));
|
||||
opts.MemberFilter = target.Id;
|
||||
|
||||
var title = new StringBuilder($"Groups containing {target.Name} (`{target.DisplayHid(ctx.Config)}`) in ");
|
||||
var title = new StringBuilder($"Groups containing {target.NameFor(ctx)} (`{target.DisplayHid(ctx.Config)}`) in ");
|
||||
if (ctx.Guild != null)
|
||||
{
|
||||
var guildSettings = await ctx.Repository.GetSystemGuild(ctx.Guild.Id, targetSystem.Id);
|
||||
|
|
|
|||
|
|
@ -442,56 +442,7 @@ public class ProxiedMessage
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
MessageComponent authorInfo;
|
||||
var author = user != null
|
||||
? $"{user.Username}#{user.Discriminator}"
|
||||
: $"Deleted user ${message.Message.Sender}";
|
||||
var avatarUrl = user?.AvatarUrl();
|
||||
var authorString = $"{author}\n**ID: **`{message.Message.Sender.ToString()}`";
|
||||
if (user != null && avatarUrl != "")
|
||||
{
|
||||
authorInfo = new MessageComponent()
|
||||
{
|
||||
Type = ComponentType.Section,
|
||||
Components = [
|
||||
new MessageComponent()
|
||||
{
|
||||
Type = ComponentType.Text,
|
||||
Content = authorString
|
||||
}
|
||||
],
|
||||
Accessory = new MessageComponent()
|
||||
{
|
||||
Type = ComponentType.Thumbnail,
|
||||
Media = new ComponentMedia()
|
||||
{
|
||||
Url = avatarUrl
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
authorInfo = new MessageComponent()
|
||||
{
|
||||
Type = ComponentType.Text,
|
||||
Content = authorString
|
||||
};
|
||||
}
|
||||
MessageComponent container = new MessageComponent()
|
||||
{
|
||||
Type = ComponentType.Container,
|
||||
Components = [
|
||||
authorInfo,
|
||||
]
|
||||
};
|
||||
|
||||
await ctx.Reply(components: [new MessageComponent()
|
||||
{
|
||||
Type = ComponentType.Text,
|
||||
Content = user != null ? $"{user.Mention()} ({user.Id})" : $"*(deleted user {message.Message.Sender})*"
|
||||
},container]);
|
||||
await ctx.Reply(components: await _embeds.CreateAuthorMessageComponents(user, message));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -533,6 +484,11 @@ public class ProxiedMessage
|
|||
else if (!await ctx.CheckPermissionsInGuildChannel(channel, PermissionSet.ViewChannel))
|
||||
showContent = false;
|
||||
|
||||
await ctx.Reply(embed: await _embeds.CreateCommandMessageInfoEmbed(msg, showContent));
|
||||
if (ctx.MatchFlag("show-embed", "se"))
|
||||
{
|
||||
await ctx.Reply(embed: await _embeds.CreateCommandMessageInfoEmbed(msg, showContent));
|
||||
return;
|
||||
}
|
||||
await ctx.Reply(components: await _embeds.CreateCommandMessageInfoMessageComponents(msg, showContent));
|
||||
}
|
||||
}
|
||||
|
|
@ -186,22 +186,6 @@ public class ReactionAdded: IEventHandler<MessageReactionAddEvent>
|
|||
{
|
||||
var dm = await _dmCache.GetOrCreateDmChannel(evt.UserId);
|
||||
|
||||
// var embeds = new List<Embed>();
|
||||
|
||||
// if (msg.Member != null)
|
||||
// embeds.Add(await _embeds.CreateMemberEmbed(
|
||||
// msg.System,
|
||||
// msg.Member,
|
||||
// guild,
|
||||
// config,
|
||||
// LookupContext.ByNonOwner,
|
||||
// DateTimeZone.Utc
|
||||
// ));
|
||||
|
||||
// embeds.Add(await _embeds.CreateMessageInfoEmbed(msg, true, config));
|
||||
|
||||
// await _rest.CreateMessage(dm, new MessageRequest { Embeds = embeds.ToArray() });
|
||||
|
||||
var components = new List<MessageComponent>();
|
||||
if (msg.Member != null)
|
||||
components.AddRange(await _embeds.CreateMemberMessageComponents(
|
||||
|
|
@ -212,9 +196,12 @@ public class ReactionAdded: IEventHandler<MessageReactionAddEvent>
|
|||
LookupContext.ByNonOwner,
|
||||
DateTimeZone.Utc
|
||||
));
|
||||
|
||||
components.Add(new MessageComponent()
|
||||
{
|
||||
Type = ComponentType.Separator
|
||||
});
|
||||
components.AddRange(await _embeds.CreateMessageInfoMessageComponents(msg, true, config));
|
||||
await _rest.CreateMessage(dm, new MessageRequest { Components = components.ToArray(), Flags = Message.MessageFlags.IsComponentsV2 });
|
||||
await _rest.CreateMessage(dm, new MessageRequest { Components = components.ToArray(), Flags = Message.MessageFlags.IsComponentsV2, AllowedMentions = new AllowedMentions() });
|
||||
}
|
||||
catch (ForbiddenException) { } // No permissions to DM, can't check for this :(
|
||||
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ public class EmbedService
|
|||
var countctx = LookupContext.ByNonOwner;
|
||||
if (cctx.MatchFlag("a", "all"))
|
||||
{
|
||||
if (system.Id == cctx.System.Id)
|
||||
if (system.Id == cctx.System?.Id)
|
||||
countctx = LookupContext.ByOwner;
|
||||
else
|
||||
throw Errors.LookupNotAllowed;
|
||||
|
|
@ -89,7 +89,7 @@ public class EmbedService
|
|||
if (system.MemberListPrivacy.CanAccess(ctx))
|
||||
{
|
||||
headerText += $"\n**Members:** {memberCount}";
|
||||
if (system.Id == cctx.System.Id)
|
||||
if (system.Id == cctx.System?.Id)
|
||||
if (memberCount > 0)
|
||||
headerText += $" (see `{cctx.DefaultPrefix}system list`)";
|
||||
else
|
||||
|
|
@ -192,7 +192,7 @@ public class EmbedService
|
|||
new MessageComponent()
|
||||
{
|
||||
Type = ComponentType.Text,
|
||||
Content = $"-# System ID: `{system.DisplayHid(cctx.Config)}`\n-# Created: {system.Created.FormatZoned(cctx.Zone)}",
|
||||
Content = $"-# System ID: `{system.DisplayHid(cctx.Config)}`\n-# Created: {DiscordUtils.InstantToTimestampString(system.Created)}",
|
||||
},
|
||||
],
|
||||
Accessory = new MessageComponent()
|
||||
|
|
@ -215,7 +215,7 @@ public class EmbedService
|
|||
var countctx = LookupContext.ByNonOwner;
|
||||
if (cctx.MatchFlag("a", "all"))
|
||||
{
|
||||
if (system.Id == cctx.System.Id)
|
||||
if (system.Id == cctx.System?.Id)
|
||||
countctx = LookupContext.ByOwner;
|
||||
else
|
||||
throw Errors.LookupNotAllowed;
|
||||
|
|
@ -469,7 +469,7 @@ public class EmbedService
|
|||
new MessageComponent()
|
||||
{
|
||||
Type = ComponentType.Text,
|
||||
Content = $"-# System ID: `{system.DisplayHid(ccfg)}` \u2219 Member ID: `{member.DisplayHid(ccfg)}`{(member.MetadataPrivacy.CanAccess(ctx) ? $"\n-# Created: {member.Created.FormatZoned(zone)}" : "")}",
|
||||
Content = $"-# System ID: `{system.DisplayHid(ccfg)}` \u2219 Member ID: `{member.DisplayHid(ccfg)}`{(member.MetadataPrivacy.CanAccess(ctx) ? $"\n-# Created: {DiscordUtils.InstantToTimestampString(member.Created)}" : "")}",
|
||||
},
|
||||
],
|
||||
Accessory = new MessageComponent()
|
||||
|
|
@ -570,7 +570,7 @@ public class EmbedService
|
|||
var countctx = LookupContext.ByNonOwner;
|
||||
if (ctx.MatchFlag("a", "all"))
|
||||
{
|
||||
if (system.Id == ctx.System.Id)
|
||||
if (system.Id == ctx.System?.Id)
|
||||
countctx = LookupContext.ByOwner;
|
||||
else
|
||||
throw Errors.LookupNotAllowed;
|
||||
|
|
@ -588,7 +588,7 @@ public class EmbedService
|
|||
if (target.ListPrivacy.CanAccess(pctx))
|
||||
{
|
||||
headerText += $"\n**Members:** {memberCount}";
|
||||
if (system.Id == ctx.System.Id && memberCount == 0)
|
||||
if (system.Id == ctx.System?.Id && memberCount == 0)
|
||||
headerText += $" (add one with `{ctx.DefaultPrefix}group {target.Reference(ctx)} add <member>`!)";
|
||||
else if (memberCount > 0)
|
||||
headerText += $" (see `{ctx.DefaultPrefix}group {target.Reference(ctx)} list`)";
|
||||
|
|
@ -659,7 +659,7 @@ public class EmbedService
|
|||
new MessageComponent()
|
||||
{
|
||||
Type = ComponentType.Text,
|
||||
Content = $"-# System ID: `{system.DisplayHid(ctx.Config)}` \u2219 Group ID: `{target.DisplayHid(ctx.Config)}`{(target.MetadataPrivacy.CanAccess(pctx) ? $"\n-# Created: {target.Created.FormatZoned(ctx.Zone)}" : "")}",
|
||||
Content = $"-# System ID: `{system.DisplayHid(ctx.Config)}` \u2219 Group ID: `{target.DisplayHid(ctx.Config)}`{(target.MetadataPrivacy.CanAccess(pctx) ? $"\n-# Created: {DiscordUtils.InstantToTimestampString(target.Created)}" : "")}",
|
||||
},
|
||||
],
|
||||
Accessory = new MessageComponent()
|
||||
|
|
@ -680,7 +680,7 @@ public class EmbedService
|
|||
var countctx = LookupContext.ByNonOwner;
|
||||
if (ctx.MatchFlag("a", "all"))
|
||||
{
|
||||
if (system.Id == ctx.System.Id)
|
||||
if (system.Id == ctx.System?.Id)
|
||||
countctx = LookupContext.ByOwner;
|
||||
else
|
||||
throw Errors.LookupNotAllowed;
|
||||
|
|
@ -820,7 +820,7 @@ public class EmbedService
|
|||
new MessageComponent()
|
||||
{
|
||||
Type = ComponentType.Text,
|
||||
Content = $"**Since:** <t:{sw.Timestamp.ToUnixTimeSeconds()}> (<t:{sw.Timestamp.ToUnixTimeSeconds()}:R>)"
|
||||
Content = $"**Since:** {DiscordUtils.InstantToTimestampString(sw.Timestamp)} (<t:{sw.Timestamp.ToUnixTimeSeconds()}:R>)"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -928,7 +928,7 @@ public class EmbedService
|
|||
memberList.Add(new MessageComponent()
|
||||
{
|
||||
Type = ComponentType.Text,
|
||||
Content = $"**Since:** <t:{sw.Timestamp.ToUnixTimeSeconds()}> (<t:{sw.Timestamp.ToUnixTimeSeconds()}:R>)"
|
||||
Content = $"**Since:** {DiscordUtils.InstantToTimestampString(sw.Timestamp)} (<t:{sw.Timestamp.ToUnixTimeSeconds()}:R>)"
|
||||
});
|
||||
return [
|
||||
new MessageComponent(){
|
||||
|
|
@ -1023,7 +1023,7 @@ public class EmbedService
|
|||
};
|
||||
|
||||
var avatarURL = msg.Member?.AvatarFor(ctx).TryGetCleanCdnUrl();
|
||||
MessageComponent header = avatarURL == "" ? authorData : new MessageComponent()
|
||||
MessageComponent header = (avatarURL == "" || avatarURL == null) ? authorData : new MessageComponent()
|
||||
{
|
||||
Type = ComponentType.Section,
|
||||
Components = [authorData],
|
||||
|
|
@ -1042,39 +1042,53 @@ public class EmbedService
|
|||
{
|
||||
Type = ComponentType.Separator,
|
||||
Spacing = 2
|
||||
},
|
||||
new MessageComponent()
|
||||
}
|
||||
];
|
||||
if (content != "")
|
||||
{
|
||||
body.Add(new MessageComponent()
|
||||
{
|
||||
Type = ComponentType.Text,
|
||||
Content = content
|
||||
}
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
if (showContent)
|
||||
{
|
||||
var url = serverMsg?.Attachments?.FirstOrDefault()?.Url;
|
||||
if (url != null && url != "")
|
||||
body.Add(new MessageComponent()
|
||||
if (serverMsg != null)
|
||||
{
|
||||
var media = new List<ComponentMediaItem>();
|
||||
foreach (Message.Attachment attachment in serverMsg?.Attachments)
|
||||
{
|
||||
Type = ComponentType.MediaGallery,
|
||||
Items = [new ComponentMediaItem()
|
||||
var url = attachment.Url;
|
||||
if (url != null && url != "")
|
||||
media.Add(new ComponentMediaItem()
|
||||
{
|
||||
Media = new ComponentMedia()
|
||||
{
|
||||
Url = url
|
||||
}
|
||||
});
|
||||
}
|
||||
if (media.Count > 0)
|
||||
body.Add(new MessageComponent()
|
||||
{
|
||||
Media = new ComponentMedia(){
|
||||
Url = url
|
||||
}
|
||||
}]
|
||||
});
|
||||
Type = ComponentType.MediaGallery,
|
||||
Items = media.ToArray()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
MessageComponent footer = new MessageComponent()
|
||||
{
|
||||
Type = ComponentType.Text,
|
||||
Content = $"-# Original Message ID: {msg.Message.OriginalMid} · <t:{DiscordUtils.SnowflakeToTimestamp(msg.Message.Mid)}:t>"
|
||||
Content = $"-# Original Message ID: {msg.Message.OriginalMid} · {DiscordUtils.InstantToTimestampString(DiscordUtils.SnowflakeToInstant(msg.Message.Mid))}"
|
||||
};
|
||||
|
||||
return [
|
||||
new MessageComponent(){
|
||||
Type = ComponentType.Container,
|
||||
new MessageComponent()
|
||||
{
|
||||
Type = ComponentType.Container,
|
||||
Components = [
|
||||
header,
|
||||
..body
|
||||
|
|
@ -1169,6 +1183,106 @@ public class EmbedService
|
|||
return eb.Build();
|
||||
}
|
||||
|
||||
public async Task<MessageComponent[]> CreateAuthorMessageComponents(User? user, FullMessage msg)
|
||||
{
|
||||
MessageComponent authorInfo;
|
||||
var author = user != null
|
||||
? $"{user.Username}#{user.Discriminator}"
|
||||
: $"Deleted user ${msg.Message.Sender}";
|
||||
var avatarUrl = user?.AvatarUrl();
|
||||
var authorString = $"{author}\n**ID: **`{msg.Message.Sender.ToString()}`";
|
||||
if (user != null && avatarUrl != "")
|
||||
{
|
||||
authorInfo = new MessageComponent()
|
||||
{
|
||||
Type = ComponentType.Section,
|
||||
Components = [
|
||||
new MessageComponent()
|
||||
{
|
||||
Type = ComponentType.Text,
|
||||
Content = authorString
|
||||
}
|
||||
],
|
||||
Accessory = new MessageComponent()
|
||||
{
|
||||
Type = ComponentType.Thumbnail,
|
||||
Media = new ComponentMedia()
|
||||
{
|
||||
Url = avatarUrl
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
authorInfo = new MessageComponent()
|
||||
{
|
||||
Type = ComponentType.Text,
|
||||
Content = authorString
|
||||
};
|
||||
}
|
||||
MessageComponent container = new MessageComponent()
|
||||
{
|
||||
Type = ComponentType.Container,
|
||||
Components = [
|
||||
authorInfo,
|
||||
]
|
||||
};
|
||||
return (
|
||||
[
|
||||
new MessageComponent()
|
||||
{
|
||||
Type = ComponentType.Text,
|
||||
Content = user != null ? $"{user.Mention()} ({user.Id})" : $"*(deleted user {msg.Message.Sender})*"
|
||||
},
|
||||
container
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public async Task<MessageComponent[]> CreateCommandMessageInfoMessageComponents(Core.CommandMessage msg, bool showContent)
|
||||
{
|
||||
var content = "*(command message deleted or inaccessible)*";
|
||||
if (showContent)
|
||||
{
|
||||
var discordMessage = await _rest.GetMessageOrNull(msg.Channel, msg.OriginalMid);
|
||||
if (discordMessage != null)
|
||||
content = discordMessage.Content;
|
||||
}
|
||||
|
||||
List<MessageComponent> body = [
|
||||
new MessageComponent()
|
||||
{
|
||||
Type = ComponentType.Text,
|
||||
Content = $"### Command response message\n**Original message:** https://discord.com/channels/{msg.Guild}/{msg.Channel}/{msg.OriginalMid}\n**Sent By:** <@{msg.Sender}>"
|
||||
},
|
||||
new MessageComponent()
|
||||
{
|
||||
Type = ComponentType.Separator,
|
||||
},
|
||||
new MessageComponent()
|
||||
{
|
||||
Type = ComponentType.Text,
|
||||
Content = content
|
||||
},
|
||||
];
|
||||
|
||||
MessageComponent footer = new MessageComponent()
|
||||
{
|
||||
Type = ComponentType.Text,
|
||||
Content = $"-# Original Message ID: {msg.OriginalMid} · {DiscordUtils.InstantToTimestampString(DiscordUtils.SnowflakeToInstant(msg.OriginalMid))}"
|
||||
};
|
||||
|
||||
return [
|
||||
new MessageComponent(){
|
||||
Type = ComponentType.Container,
|
||||
Components = [
|
||||
..body
|
||||
]
|
||||
},
|
||||
footer
|
||||
];
|
||||
}
|
||||
public async Task<Embed> CreateCommandMessageInfoEmbed(Core.CommandMessage msg, bool showContent)
|
||||
{
|
||||
var content = "*(command message deleted or inaccessible)*";
|
||||
|
|
|
|||
|
|
@ -45,6 +45,9 @@ public static class DiscordUtils
|
|||
public static ulong InstantToSnowflake(Instant time) =>
|
||||
(ulong)(time - Instant.FromUtc(2015, 1, 1, 0, 0, 0)).TotalMilliseconds << 22;
|
||||
|
||||
public static string InstantToTimestampString(Instant time) =>
|
||||
$"<t:{time.ToUnixTimeSeconds()}:f>";
|
||||
|
||||
public static async Task CreateReactionsBulk(this DiscordApiClient rest, Message msg, string[] reactions)
|
||||
{
|
||||
foreach (var reaction in reactions)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using Autofac;
|
|||
using Myriad.Cache;
|
||||
using Myriad.Gateway;
|
||||
using Myriad.Rest;
|
||||
using Myriad.Rest.Types;
|
||||
using Myriad.Types;
|
||||
|
||||
using PluralKit.Core;
|
||||
|
|
@ -76,13 +77,14 @@ public class InteractionContext
|
|||
});
|
||||
}
|
||||
|
||||
public async Task Reply(MessageComponent[] components = null)
|
||||
public async Task Reply(MessageComponent[] components = null, AllowedMentions? mentions = null)
|
||||
{
|
||||
await Respond(InteractionResponse.ResponseType.ChannelMessageWithSource,
|
||||
new InteractionApplicationCommandCallbackData
|
||||
{
|
||||
Components = components,
|
||||
Flags = Message.MessageFlags.Ephemeral | Message.MessageFlags.IsComponentsV2
|
||||
Flags = Message.MessageFlags.Ephemeral | Message.MessageFlags.IsComponentsV2,
|
||||
AllowedMentions = mentions ?? new AllowedMentions()
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use std::sync::Arc;
|
|||
use tokio::sync::mpsc::Sender;
|
||||
use tracing::{error, info, warn};
|
||||
use twilight_gateway::{
|
||||
CloseFrame, ConfigBuilder, Event, EventTypeFlags, Message, Shard, ShardId, create_iterator,
|
||||
ConfigBuilder, Event, EventTypeFlags, Message, Shard, ShardId, create_iterator,
|
||||
};
|
||||
use twilight_model::gateway::{
|
||||
Intents,
|
||||
|
|
@ -118,8 +118,11 @@ pub async fn runner(
|
|||
Message::Close(frame) => {
|
||||
let mut state_event = ShardStateEvent::Closed;
|
||||
let close_code = if let Some(close) = frame {
|
||||
if close == CloseFrame::RESUME {
|
||||
state_event = ShardStateEvent::Reconnect;
|
||||
match close.code {
|
||||
4000..=4003 | 4005..=4009 => {
|
||||
state_event = ShardStateEvent::Reconnect;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
close.code.to_string()
|
||||
} else {
|
||||
|
|
@ -176,32 +179,45 @@ pub async fn runner(
|
|||
)
|
||||
.increment(1);
|
||||
|
||||
// update shard state and discord cache
|
||||
if matches!(event, Event::Ready(_)) || matches!(event, Event::Resumed) {
|
||||
if let Err(error) = tx_state.try_send((
|
||||
shard.id(),
|
||||
ShardStateEvent::Other,
|
||||
Some(event.clone()),
|
||||
None,
|
||||
)) {
|
||||
tracing::error!(?error, "error updating shard state");
|
||||
// check for shard status events
|
||||
match event {
|
||||
Event::Ready(_) | Event::Resumed => {
|
||||
if let Err(error) = tx_state.try_send((
|
||||
shard.id(),
|
||||
ShardStateEvent::Other,
|
||||
Some(event.clone()),
|
||||
None,
|
||||
)) {
|
||||
tracing::error!(?error, "error updating shard state");
|
||||
}
|
||||
}
|
||||
}
|
||||
// need to do heartbeat separately, to get the latency
|
||||
let latency_num = shard
|
||||
.latency()
|
||||
.recent()
|
||||
.first()
|
||||
.map_or_else(|| 0, |d| d.as_millis()) as i32;
|
||||
if let Event::GatewayHeartbeatAck = event
|
||||
&& let Err(error) = tx_state.try_send((
|
||||
shard.id(),
|
||||
ShardStateEvent::Heartbeat,
|
||||
Some(event.clone()),
|
||||
Some(latency_num),
|
||||
))
|
||||
{
|
||||
tracing::error!(?error, "error updating shard state for latency");
|
||||
Event::GatewayReconnect => {
|
||||
if let Err(error) = tx_state.try_send((
|
||||
shard.id(),
|
||||
ShardStateEvent::Reconnect,
|
||||
Some(event.clone()),
|
||||
None,
|
||||
)) {
|
||||
tracing::error!(?error, "error updating shard state for reconnect");
|
||||
}
|
||||
}
|
||||
Event::GatewayHeartbeatAck => {
|
||||
// need to do heartbeat separately, to get the latency
|
||||
let latency_num = shard
|
||||
.latency()
|
||||
.recent()
|
||||
.first()
|
||||
.map_or_else(|| 0, |d| d.as_millis()) as i32;
|
||||
if let Err(error) = tx_state.try_send((
|
||||
shard.id(),
|
||||
ShardStateEvent::Heartbeat,
|
||||
Some(event.clone()),
|
||||
Some(latency_num),
|
||||
)) {
|
||||
tracing::error!(?error, "error updating shard state for latency");
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if let Event::Ready(_) = event {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
-- database version 54
|
||||
-- add config option for list format
|
||||
-- add short/full option for fronter list
|
||||
|
||||
alter table system_config add column fronter_list_format text default 'short';
|
||||
alter table system_config add column fronter_list_format int default 0;
|
||||
|
||||
update info set schema_version = 54;
|
||||
|
|
@ -99,4 +99,9 @@ It is not possible to edit messages via ID. Please use the full link, or reply t
|
|||
You cannot reply-@ a proxied messages due to their nature as webhooks. If you want to "reply-@" a proxied message, you must react to the message with 🔔, 🛎, or 🏓. This will send a message from PluralKit that reads "Psst, MEMBER (@User), you have been pinged by @You", which will ping the Discord account behind the proxied message.
|
||||
|
||||
### Why do most of PluralKit's messages look blank or empty?
|
||||
A lot of PluralKit's command responses use Discord embeds. If you can't see them, it's likely you have embeds turned off. To change this, go into your discord settings and find the tab "Chat" under "App Settings". Find the setting "Show embeds and preview website links" and turn it on. If it's already on, try turning it off and then on again.
|
||||
PluralKit now uses Discord's "Components V2" for system/member/group cards - if the cards no longer show, your Discord app is too old to show the new components, and you should update it.
|
||||
A temporary workaround to show the old version of the cards exists as the -show-embed (or -se) flag to pk;system / pk;member / pk;group - however, we will be removing the old embed-based cards in the future (and as such, we will not add a config option to always use the old cards).
|
||||
|
||||
Please read the announcement post for more details: <https://pluralkit.me/posts/2025-09-08-components-v2/>
|
||||
|
||||
Some of PluralKit's command responses still use Discord embeds. If you can't see them, it's likely you have embeds turned off. To change this, go into your discord settings and find the tab "Chat" under "App Settings". Find the setting "Show embeds and preview website links" and turn it on. If it's already on, try turning it off and then on again.
|
||||
Loading…
Add table
Add a link
Reference in a new issue