mirror of
https://github.com/PluralKit/PluralKit.git
synced 2026-02-04 04:56:49 +00:00
Compare commits
8 commits
492cce6223
...
09ed215e6c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
09ed215e6c | ||
|
|
ce0983ba16 | ||
|
|
4a947c01fc | ||
|
|
4973c0b992 | ||
|
|
f61731a915 | ||
|
|
3e1a310884 | ||
|
|
952bb02285 | ||
|
|
9dfbf64dac |
14 changed files with 151 additions and 29 deletions
|
|
@ -35,7 +35,10 @@ public class BaseRestClient: IAsyncDisposable
|
|||
if (!token.StartsWith("Bot "))
|
||||
token = "Bot " + token;
|
||||
|
||||
Client = new HttpClient();
|
||||
Client = new HttpClient(new SocketsHttpHandler
|
||||
{
|
||||
PooledConnectionIdleTimeout = TimeSpan.FromSeconds(3),
|
||||
});
|
||||
Client.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", userAgent);
|
||||
Client.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", token);
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ using Autofac.Extensions.DependencyInjection;
|
|||
|
||||
using PluralKit.Core;
|
||||
|
||||
using Sentry;
|
||||
|
||||
using Serilog;
|
||||
|
||||
namespace PluralKit.API;
|
||||
|
|
@ -21,9 +23,16 @@ public class Program
|
|||
opts.Dsn = config.SentryUrl ?? "";
|
||||
opts.Release = BuildInfoService.FullVersion;
|
||||
opts.AutoSessionTracking = true;
|
||||
// opts.DisableTaskUnobservedTaskExceptionCapture();
|
||||
opts.DisableUnobservedTaskExceptionCapture();
|
||||
});
|
||||
|
||||
TaskScheduler.UnobservedTaskException += (_, e) =>
|
||||
{
|
||||
foreach (var inner in e.Exception.Flatten().InnerExceptions)
|
||||
SentrySdk.CaptureException(inner);
|
||||
e.SetObserved();
|
||||
};
|
||||
|
||||
await host.Services.GetRequiredService<RedisService>().InitAsync(config);
|
||||
await host.RunAsync();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -442,7 +442,7 @@ public class ProxiedMessage
|
|||
return;
|
||||
}
|
||||
|
||||
await ctx.Reply(components: await _embeds.CreateAuthorMessageComponents(user, message));
|
||||
await ctx.Reply(components: _embeds.CreateAuthorMessageComponents(user, message));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -58,13 +58,19 @@ public class MessageEdited: IEventHandler<MessageUpdateEvent>
|
|||
|
||||
var channel = await _cache.TryGetChannel(guildId, evt.ChannelId); // todo: is this correct for message update?
|
||||
if (channel == null)
|
||||
throw new Exception("could not find self channel in MessageEdited event");
|
||||
{
|
||||
_logger.Warning("could not find self channel in MessageEdited event");
|
||||
return;
|
||||
}
|
||||
if (!DiscordUtils.IsValidGuildChannel(channel))
|
||||
return;
|
||||
var rootChannel = await _cache.GetRootChannel(guildId, channel.Id);
|
||||
var guild = await _cache.TryGetGuild(channel.GuildId!.Value);
|
||||
if (guild == null)
|
||||
throw new Exception("could not find self guild in MessageEdited event");
|
||||
{
|
||||
_logger.Warning("could not find self guild in MessageEdited event");
|
||||
return;
|
||||
}
|
||||
var lastMessage = (await _lastMessageCache.GetLastMessage(evt.GuildId.HasValue ? evt.GuildId.Value ?? 0 : 0, evt.ChannelId))?.Current;
|
||||
|
||||
// Only react to the last message in the channel
|
||||
|
|
|
|||
|
|
@ -45,9 +45,16 @@ public class Init
|
|||
opts.Dsn = services.Resolve<CoreConfig>().SentryUrl ?? "";
|
||||
opts.Release = BuildInfoService.FullVersion;
|
||||
opts.AutoSessionTracking = true;
|
||||
// opts.DisableTaskUnobservedTaskExceptionCapture();
|
||||
opts.DisableUnobservedTaskExceptionCapture();
|
||||
});
|
||||
|
||||
TaskScheduler.UnobservedTaskException += (_, e) =>
|
||||
{
|
||||
foreach (var inner in e.Exception.Flatten().InnerExceptions)
|
||||
SentrySdk.CaptureException(inner);
|
||||
e.SetObserved();
|
||||
};
|
||||
|
||||
var config = services.Resolve<BotConfig>();
|
||||
var coreConfig = services.Resolve<CoreConfig>();
|
||||
|
||||
|
|
|
|||
|
|
@ -324,6 +324,12 @@ public class ProxyService
|
|||
// Mangle embeds (for reply embed color changing)
|
||||
var mangledEmbeds = originalMsg.Embeds!.Select(embed => MangleReproxyEmbed(embed, member)).Where(embed => embed != null).ToArray();
|
||||
|
||||
Message.MessageFlags flags = 0;
|
||||
if (originalMsg.Flags.HasFlag(Message.MessageFlags.SuppressNotifications))
|
||||
flags |= Message.MessageFlags.SuppressNotifications;
|
||||
if (originalMsg.Flags.HasFlag(Message.MessageFlags.VoiceMessage))
|
||||
flags |= Message.MessageFlags.VoiceMessage;
|
||||
|
||||
// Send the reproxied webhook
|
||||
var proxyMessage = await _webhookExecutor.ExecuteWebhook(new ProxyRequest
|
||||
{
|
||||
|
|
@ -339,7 +345,7 @@ public class ProxyService
|
|||
Embeds = mangledEmbeds,
|
||||
Stickers = originalMsg.StickerItems!,
|
||||
AllowEveryone = allowEveryone,
|
||||
Flags = originalMsg.Flags.HasFlag(Message.MessageFlags.VoiceMessage) ? Message.MessageFlags.VoiceMessage : null,
|
||||
Flags = flags,
|
||||
Tts = tts,
|
||||
Poll = originalMsg.Poll,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1004,7 +1004,7 @@ public class EmbedService
|
|||
return eb.Build();
|
||||
}
|
||||
|
||||
public async Task<MessageComponent[]> CreateAuthorMessageComponents(User? user, FullMessage msg)
|
||||
public MessageComponent[] CreateAuthorMessageComponents(User? user, FullMessage msg)
|
||||
{
|
||||
MessageComponent authorInfo;
|
||||
var author = user != null
|
||||
|
|
@ -1049,16 +1049,15 @@ public class EmbedService
|
|||
authorInfo,
|
||||
]
|
||||
};
|
||||
return (
|
||||
[
|
||||
new MessageComponent()
|
||||
{
|
||||
Type = ComponentType.Text,
|
||||
Content = user != null ? $"{user.Mention()} ({user.Id})" : $"*(deleted user {msg.Message.Sender})*"
|
||||
},
|
||||
container
|
||||
]
|
||||
);
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ public class LastMessageCacheService
|
|||
public async Task<CacheEntry?> GetLastMessage(ulong guild, ulong channel)
|
||||
{
|
||||
if (_maybeHttp is HttpDiscordCache)
|
||||
return await (_maybeHttp as HttpDiscordCache).GetLastMessage<CacheEntry>(guild, channel);
|
||||
return await (_maybeHttp as HttpDiscordCache)!.GetLastMessage<CacheEntry>(guild, channel);
|
||||
|
||||
return _cache.TryGetValue(channel, out var message) ? message : null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ public partial class ModelRepository
|
|||
|
||||
public Task<PKMember?> GetMemberByHid(string hid, SystemId? system = null)
|
||||
{
|
||||
var query = new Query("members").Where("hid", hid.ToLower());
|
||||
var query = new Query("members").WhereRaw("hid = (?)::char(6)", hid.ToLower());
|
||||
if (system != null)
|
||||
query = query.Where("system", system);
|
||||
return _db.QueryFirst<PKMember?>(query);
|
||||
|
|
|
|||
BIN
docs/content/assets/premiumTeaser.png
Normal file
BIN
docs/content/assets/premiumTeaser.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 130 KiB |
89
docs/content/posts/2026-01-16-pluralkit-premium.md
Normal file
89
docs/content/posts/2026-01-16-pluralkit-premium.md
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
---
|
||||
title: Announcing PluralKit Premium
|
||||
permalink: /posts/2026-01-16-pluralkit-premium/
|
||||
---
|
||||
|
||||
## Announcing PluralKit Premium
|
||||
|
||||
As we've teased earlier in [the support server](https://discord.gg/PczBt78),
|
||||
we will be adding a "premium" subscription tier to PluralKit. We do want to
|
||||
assure everyone, however, that **the bot will always be free to use, and no
|
||||
existing features (or new core features) will ever be paywalled.**
|
||||
|
||||
Our goal for PK Premium is to have the income from subscriptions cover all the
|
||||
running costs of the bot - which, up until now, has been paid for partly by
|
||||
donations to our [Patreon](https://patreon.com/pluralkit) /
|
||||
[Buy Me A Coffee](https://buymeacoffee.com/pluralkit); and partly out of the
|
||||
pockets of the developers.
|
||||
|
||||
PluralKit Premium will cost **US$5/month** (plus tax), and will be **launching
|
||||
before the end of February 2026**.
|
||||
|
||||
PluralKit Premium will offer both cosmetic perks, and "power user" features.
|
||||
At launch, PK Premium will offer the following:
|
||||
|
||||
- the ability to set custom system/member/group IDs,
|
||||
- lossless, higher resolution image hosting on PluralKit's CDN,
|
||||
- the ability to upload avatars/banners directly from the PluralKit Dashboard,
|
||||
- and a badge on your PluralKit system card to show off your support.
|
||||
|
||||
We will be adding more perks to PK Premium in future - including, but not
|
||||
limited to:
|
||||
- more description customisation options,
|
||||
- automatic regex-based proxy text substitution ("automated typing quirks"),
|
||||
- and more!
|
||||
|
||||
<div style="text-align:center;">
|
||||
|
||||

|
||||
|
||||
</div>
|
||||
|
||||
### FAQs
|
||||
|
||||
#### I didn't read any of the stuff above, give me a tl;dr!
|
||||
|
||||
- PK Premium is launching before the end of February 2026, at US$5/month (+ tax)
|
||||
- No existing features, or new core features, will be paywalled - the premium
|
||||
subscription offers cosmetic perks and power-user features only
|
||||
- Features at launch include custom IDs; high-resolution image hosting; and
|
||||
direct image upload from the web dashboard
|
||||
- More premium features are still to come!
|
||||
|
||||
#### Can I pay yearly?
|
||||
|
||||
Yes, there will be a yearly subscription option. There is no discount for paying
|
||||
yearly.
|
||||
|
||||
#### Can I gift a PK Premium subscription to someone?
|
||||
|
||||
Not at launch, but we will likely be revisiting this in future.
|
||||
|
||||
#### How will ID changes work?
|
||||
|
||||
There will be bot commands for changing your own system, member, and group IDs.
|
||||
|
||||
Each month of PK Premium you pay for will grant you a number of ID change "tokens,"
|
||||
and each ID you change uses one of those "tokens." The exact number of ID changes
|
||||
you will get each month has not yet been confirmed, but they will stack over time.
|
||||
|
||||
In addition to the "token" system, to ensure fairness, there is a cap on the number
|
||||
of IDs you can change in a 24-hour period.
|
||||
|
||||
#### Can I transfer custom IDs to another system?
|
||||
|
||||
No, sorry.
|
||||
|
||||
#### If I subscribe; customise my IDs; and then cancel, do I keep my custom IDs?
|
||||
|
||||
Yes!
|
||||
|
||||
#### How does this affect PluralKit being open source?
|
||||
|
||||
PluralKit will remain open source, and there is no change to the licensing.
|
||||
Premium features are included in the open source code.
|
||||
|
||||
#### I have another question!
|
||||
|
||||
Please ask in [#bot-support in the PluralKit support server](https://discord.gg/PczBt78),
|
||||
and we'll update this FAQ!
|
||||
|
|
@ -4,6 +4,7 @@ title: Announcements & other posts
|
|||
|
||||
# Announcements & other posts
|
||||
|
||||
- 2026-01-16: [Announcing PluralKit Premium](/posts/2026-01-16-pluralkit-premium/)
|
||||
- 2025-09-08: [on the switch to Components V2](/posts/2025-09-08-components-v2/)
|
||||
- 2025-01-14: [january 2025 funding update](/posts/2025-01-14-funding-update/)
|
||||
- 2024-12-05: [late 2024 downtime notes & funding update](/posts/2024-12-05-downtime-notes/)
|
||||
|
|
|
|||
|
|
@ -2,21 +2,23 @@
|
|||
|
||||
PluralKit requires some channel permissions in order to function properly:
|
||||
|
||||
- Message proxying requires the **Manage Messages** and **Manage Webhooks** permissions in a channel.
|
||||
- Most commands require the **Embed Links**, **Attach Files** and **Add Reactions** permissions to function properly.
|
||||
- *Everything* PluralKit does aside from the Message Info app command requires **View Channel** permissions in a channel.
|
||||
- Message proxying requires the **Manage Messages**, **Manage Webhooks**, and **Send Messages** permissions in a channel.
|
||||
- Most commands require the **Embed Links** and **Add Reactions** permissions to function properly.
|
||||
- Commands with reaction menus also require **Manage Messages** to remove reactions after clicking.
|
||||
- Commands executed via reactions (for example the :x:, :bell:, and :question: reactions, as well as any commands with reaction menus) need **Read Message History** to be able to see that reactions were added.
|
||||
- A couple commands (`pk;s color` and `pk;m <name> color`) currently require **Attach Files**.
|
||||
- [Proxy logging](/staff/logging) requires the **Send Messages** permission in the log channel.
|
||||
- [Log cleanup](/staff/compatibility/#log-cleanup) requires the **Manage Messages** permission in the log channels.
|
||||
|
||||
Denying the **Send Messages** permission will *not* stop the bot from proxying, although it will prevent it from sending command responses. Denying the **Read Messages** permission will, as any other bot, prevent the bot from interacting in that channel at all.
|
||||
|
||||
## Webhook permissions
|
||||
Webhooks exist outside of the normal Discord permissions system, and (with a few exceptions) it's not possible to modify their permissions.
|
||||
Webhooks exist outside of the normal Discord permissions system, but as of August 2022 they mostly follow the permissions of the webhook owner (in this case, PluralKit).
|
||||
|
||||
However, PluralKit will make an attempt to apply the sender account's permissions to proxied messages. For example, role mentions, `@everyone`, and `@here`
|
||||
PluralKit will also make an attempt to apply the sender account's permissions to proxied messages. For example, role mentions, `@everyone`, and `@here`
|
||||
will only function if the sender account has that permission. The same applies to link embeds.
|
||||
|
||||
For external emojis to work in proxied messages, the `@everyone` role must have the "Use External Emojis" permission. If it still doesn't work, check if the permission was denied in channel-specific permission settings.
|
||||
For external emojis to work in proxied messages, PluralKit or one of its roles must have the "Use External Emojis" permission. If it still doesn't work,
|
||||
check if the permission was denied in channel-specific permission settings. PluralKit must also be in the server the external emoji belongs to.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
|
@ -30,4 +32,4 @@ For example:
|
|||
|
||||
pk;debug permissions 466707357099884544
|
||||
|
||||
You can find this ID [by enabling Developer Mode and right-clicking (or long-pressing) on the server icon](https://discordia.me/developer-mode).
|
||||
You can find this ID [by enabling Developer Mode and right-clicking (or long-pressing) on the server icon](https://discordia.me/developer-mode).
|
||||
|
|
|
|||
|
|
@ -453,7 +453,7 @@ You can
|
|||
|
||||
#### Pinging the user who sent it
|
||||
If you'd like to "ping" the account behind a proxied message without having to query the message and ping them yourself,
|
||||
you can react to the message with the `:bell:` :bell: emoji (or `:bellhop:` :bellhop:, `:exclamation:` :exclamation:, or even `:ping_pong:` :ping_pong:), and PluralKit will ping the relevant member and account in the same channel on your behalf with a link to the message you reacted to.
|
||||
you can react to the message with the `:bell:` :bell: emoji (or `:bellhop:` :bellhop_bell:, `:exclamation:` :exclamation:, or even `:ping_pong:` :ping_pong:), and PluralKit will ping the relevant member and account in the same channel on your behalf with a link to the message you reacted to.
|
||||
|
||||
## Autoproxy
|
||||
The bot's *autoproxy* feature allows you to have messages be proxied without directly including the proxy tags. Autoproxy can be set up in various ways. There are three autoproxy modes currently implemented:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue