bot: split bot namespace, add system card, fix command handling
This commit is contained in:
parent
c36cee6f28
commit
62cde789cb
@ -15,7 +15,7 @@ using Npgsql.TypeHandling;
|
||||
using Npgsql.TypeMapping;
|
||||
using NpgsqlTypes;
|
||||
|
||||
namespace PluralKit
|
||||
namespace PluralKit.Bot
|
||||
{
|
||||
class Initialize
|
||||
{
|
||||
@ -58,6 +58,7 @@ namespace PluralKit
|
||||
.AddSingleton<Bot>()
|
||||
|
||||
.AddSingleton<CommandService>()
|
||||
.AddSingleton<EmbedService>()
|
||||
.AddSingleton<LogChannelService>()
|
||||
.AddSingleton<ProxyService>()
|
||||
|
||||
@ -67,7 +68,6 @@ namespace PluralKit
|
||||
.BuildServiceProvider();
|
||||
}
|
||||
|
||||
|
||||
class Bot
|
||||
{
|
||||
private IServiceProvider _services;
|
@ -3,7 +3,7 @@ using System.Threading.Tasks;
|
||||
using Dapper;
|
||||
using Discord.Commands;
|
||||
|
||||
namespace PluralKit.Commands
|
||||
namespace PluralKit.Bot.Commands
|
||||
{
|
||||
[Group("system")]
|
||||
public class SystemCommands : ContextParameterModuleBase<PKSystem>
|
||||
@ -11,24 +11,36 @@ namespace PluralKit.Commands
|
||||
public override string Prefix => "system";
|
||||
public SystemStore Systems {get; set;}
|
||||
public MemberStore Members {get; set;}
|
||||
public EmbedService EmbedService {get; set;}
|
||||
|
||||
private RuntimeResult NO_SYSTEM_ERROR => PKResult.Error($"You do not have a system registered with PluralKit. To create one, type `pk;system new`. If you already have a system registered on another account, type `pk;link {Context.User.Mention}` from that account to link it here.");
|
||||
private RuntimeResult OTHER_SYSTEM_CONTEXT_ERROR => PKResult.Error("You can only run this command on your own system.");
|
||||
|
||||
[Command]
|
||||
public async Task<RuntimeResult> Query(PKSystem system = null) {
|
||||
if (system == null) system = Context.SenderSystem;
|
||||
if (system == null) return NO_SYSTEM_ERROR;
|
||||
|
||||
await Context.Channel.SendMessageAsync(embed: await EmbedService.CreateEmbed(system));
|
||||
return PKResult.Success();
|
||||
}
|
||||
|
||||
[Command("new")]
|
||||
public async Task<RuntimeResult> New([Remainder] string systemName = null)
|
||||
{
|
||||
if (Context.ContextEntity != null) return OTHER_SYSTEM_CONTEXT_ERROR;
|
||||
if (ContextEntity != null) return OTHER_SYSTEM_CONTEXT_ERROR;
|
||||
if (Context.SenderSystem != null) return PKResult.Error("You already have a system registered with PluralKit. To view it, type `pk;system`. If you'd like to delete your system and start anew, type `pk;system delete`, or if you'd like to unlink this account from it, type `pk;unlink.");
|
||||
|
||||
var system = await Systems.Create(systemName);
|
||||
await Systems.Link(system, Context.User.Id);
|
||||
|
||||
await ReplyAsync("Your system has been created. Type `pk;system` to view it, and type `pk;help` for more information about commands you can use now.");
|
||||
return PKResult.Success();
|
||||
}
|
||||
|
||||
[Command("name")]
|
||||
public async Task<RuntimeResult> Name([Remainder] string newSystemName = null) {
|
||||
if (Context.ContextEntity != null) return OTHER_SYSTEM_CONTEXT_ERROR;
|
||||
if (ContextEntity != null) return OTHER_SYSTEM_CONTEXT_ERROR;
|
||||
if (Context.SenderSystem == null) return NO_SYSTEM_ERROR;
|
||||
if (newSystemName != null && newSystemName.Length > 250) return PKResult.Error($"Your chosen system name is too long. ({newSystemName.Length} > 250 characters)");
|
||||
|
||||
@ -39,7 +51,7 @@ namespace PluralKit.Commands
|
||||
|
||||
[Command("description")]
|
||||
public async Task<RuntimeResult> Description([Remainder] string newDescription = null) {
|
||||
if (Context.ContextEntity != null) return OTHER_SYSTEM_CONTEXT_ERROR;
|
||||
if (ContextEntity != null) return OTHER_SYSTEM_CONTEXT_ERROR;
|
||||
if (Context.SenderSystem == null) return NO_SYSTEM_ERROR;
|
||||
if (newDescription != null && newDescription.Length > 1000) return PKResult.Error($"Your chosen description is too long. ({newDescription.Length} > 250 characters)");
|
||||
|
||||
@ -50,7 +62,7 @@ namespace PluralKit.Commands
|
||||
|
||||
[Command("tag")]
|
||||
public async Task<RuntimeResult> Tag([Remainder] string newTag = null) {
|
||||
if (Context.ContextEntity != null) return OTHER_SYSTEM_CONTEXT_ERROR;
|
||||
if (ContextEntity != null) return OTHER_SYSTEM_CONTEXT_ERROR;
|
||||
if (Context.SenderSystem == null) return NO_SYSTEM_ERROR;
|
||||
|
||||
Context.SenderSystem.Tag = newTag;
|
35
PluralKit/Bot/Services/EmbedService.cs
Normal file
35
PluralKit/Bot/Services/EmbedService.cs
Normal file
@ -0,0 +1,35 @@
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
|
||||
namespace PluralKit.Bot {
|
||||
public class EmbedService {
|
||||
private SystemStore _systems;
|
||||
private IDiscordClient _client;
|
||||
|
||||
public EmbedService(SystemStore systems, IDiscordClient client)
|
||||
{
|
||||
this._systems = systems;
|
||||
this._client = client;
|
||||
}
|
||||
|
||||
public async Task<Embed> CreateEmbed(PKSystem system) {
|
||||
var accounts = await _systems.GetLinkedAccountIds(system);
|
||||
|
||||
// Fetch/render info for all accounts simultaneously
|
||||
var users = await Task.WhenAll(accounts.Select(async uid => (await _client.GetUserAsync(uid)).NameAndMention() ?? $"(deleted account {uid})"));
|
||||
|
||||
var eb = new EmbedBuilder()
|
||||
.WithColor(Color.Blue)
|
||||
.WithTitle(system.Name ?? null)
|
||||
.WithDescription(system.Description?.Truncate(1024))
|
||||
.WithThumbnailUrl(system.AvatarUrl ?? null)
|
||||
.WithFooter($"System ID: {system.Hid}");
|
||||
|
||||
eb.AddField("Linked accounts", string.Join(", ", users));
|
||||
eb.AddField("Members", $"(see `pk;system {system.Id} list` or `pk;system {system.Hid} list full`)");
|
||||
// TODO: fronter
|
||||
return eb.Build();
|
||||
}
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@ using System.Threading.Tasks;
|
||||
using Dapper;
|
||||
using Discord;
|
||||
|
||||
namespace PluralKit {
|
||||
namespace PluralKit.Bot {
|
||||
class ServerDefinition {
|
||||
public ulong Id;
|
||||
public ulong LogChannel;
|
@ -11,7 +11,7 @@ using Discord.Rest;
|
||||
using Discord.Webhook;
|
||||
using Discord.WebSocket;
|
||||
|
||||
namespace PluralKit
|
||||
namespace PluralKit.Bot
|
||||
{
|
||||
class ProxyDatabaseResult
|
||||
{
|
171
PluralKit/Bot/Utils.cs
Normal file
171
PluralKit/Bot/Utils.cs
Normal file
@ -0,0 +1,171 @@
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.Threading.Tasks;
|
||||
using Dapper;
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Discord.Commands.Builders;
|
||||
using Discord.WebSocket;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace PluralKit.Bot
|
||||
{
|
||||
public static class Utils {
|
||||
public static string NameAndMention(this IUser user) {
|
||||
return $"{user.Username}#{user.Discriminator} ({user.Mention})";
|
||||
}
|
||||
}
|
||||
|
||||
class UlongEncodeAsLongHandler : SqlMapper.TypeHandler<ulong>
|
||||
{
|
||||
public override ulong Parse(object value)
|
||||
{
|
||||
// Cast to long to unbox, then to ulong (???)
|
||||
return (ulong)(long)value;
|
||||
}
|
||||
|
||||
public override void SetValue(IDbDataParameter parameter, ulong value)
|
||||
{
|
||||
parameter.Value = (long)value;
|
||||
}
|
||||
}
|
||||
|
||||
class PKSystemTypeReader : TypeReader
|
||||
{
|
||||
public override async Task<TypeReaderResult> ReadAsync(ICommandContext context, string input, IServiceProvider services)
|
||||
{
|
||||
var client = services.GetService<IDiscordClient>();
|
||||
var conn = services.GetService<IDbConnection>();
|
||||
|
||||
// System references can take three forms:
|
||||
// - The direct user ID of an account connected to the system
|
||||
// - A @mention of an account connected to the system (<@uid>)
|
||||
// - A system hid
|
||||
|
||||
// First, try direct user ID parsing
|
||||
if (ulong.TryParse(input, out var idFromNumber)) return await FindSystemByAccountHelper(idFromNumber, client, conn);
|
||||
|
||||
// Then, try mention parsing.
|
||||
if (MentionUtils.TryParseUser(input, out var idFromMention)) return await FindSystemByAccountHelper(idFromMention, client, conn);
|
||||
|
||||
// Finally, try HID parsing
|
||||
var res = await conn.QuerySingleOrDefaultAsync<PKSystem>("select * from systems where hid = @Hid", new { Hid = input });
|
||||
if (res != null) return TypeReaderResult.FromSuccess(res);
|
||||
return TypeReaderResult.FromError(CommandError.ObjectNotFound, $"System with ID `{input}` not found.");
|
||||
}
|
||||
|
||||
async Task<TypeReaderResult> FindSystemByAccountHelper(ulong id, IDiscordClient client, IDbConnection conn)
|
||||
{
|
||||
var foundByAccountId = await conn.QuerySingleOrDefaultAsync<PKSystem>("select * from accounts, systems where accounts.system = system.id and accounts.id = @Id", new { Id = id });
|
||||
if (foundByAccountId != null) return TypeReaderResult.FromSuccess(foundByAccountId);
|
||||
|
||||
// We didn't find any, so we try to resolve the user ID to find the associated account,
|
||||
// so we can print their username.
|
||||
var user = await client.GetUserAsync(id);
|
||||
|
||||
// Return descriptive errors based on whether we found the user or not.
|
||||
if (user == null) return TypeReaderResult.FromError(CommandError.ObjectNotFound, $"System or account with ID `{id}` not found.");
|
||||
return TypeReaderResult.FromError(CommandError.ObjectNotFound, $"Account **{user.Username}#{user.Discriminator}** not found.");
|
||||
}
|
||||
}
|
||||
|
||||
class PKMemberTypeReader : TypeReader
|
||||
{
|
||||
public override async Task<TypeReaderResult> ReadAsync(ICommandContext context, string input, IServiceProvider services)
|
||||
{
|
||||
var conn = services.GetService(typeof(IDbConnection)) as IDbConnection;
|
||||
|
||||
// If the sender of the command is in a system themselves,
|
||||
// then try searching by the member's name
|
||||
if (context is PKCommandContext ctx && ctx.SenderSystem != null)
|
||||
{
|
||||
var foundByName = await conn.QuerySingleOrDefaultAsync<PKMember>("select * from members where system = @System and lower(name) = lower(@Name)", new { System = ctx.SenderSystem.Id, Name = input });
|
||||
if (foundByName != null) return TypeReaderResult.FromSuccess(foundByName);
|
||||
}
|
||||
|
||||
// Otherwise, if sender isn't in a system, or no member found by that name,
|
||||
// do a standard by-hid search.
|
||||
var foundByHid = await conn.QuerySingleOrDefaultAsync<PKMember>("select * from members where hid = @Hid", new { Hid = input });
|
||||
if (foundByHid != null) return TypeReaderResult.FromSuccess(foundByHid);
|
||||
return TypeReaderResult.FromError(CommandError.ObjectNotFound, "Member not found.");
|
||||
}
|
||||
}
|
||||
|
||||
/// Subclass of ICommandContext with PK-specific additional fields and functionality
|
||||
public class PKCommandContext : SocketCommandContext, ICommandContext
|
||||
{
|
||||
public IDbConnection Connection { get; }
|
||||
public PKSystem SenderSystem { get; }
|
||||
|
||||
private object _entity;
|
||||
|
||||
public PKCommandContext(DiscordSocketClient client, SocketUserMessage msg, IDbConnection connection, PKSystem system) : base(client, msg)
|
||||
{
|
||||
Connection = connection;
|
||||
SenderSystem = system;
|
||||
}
|
||||
|
||||
public T GetContextEntity<T>() where T: class {
|
||||
return _entity as T;
|
||||
}
|
||||
|
||||
public void SetContextEntity(object entity) {
|
||||
_entity = entity;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class ContextParameterModuleBase<T> : ModuleBase<PKCommandContext> where T: class
|
||||
{
|
||||
public IServiceProvider _services { get; set; }
|
||||
public CommandService _commands { get; set; }
|
||||
|
||||
public abstract string Prefix { get; }
|
||||
public abstract Task<T> ReadContextParameterAsync(string value);
|
||||
|
||||
public T ContextEntity => Context.GetContextEntity<T>();
|
||||
|
||||
protected override void OnModuleBuilding(CommandService commandService, ModuleBuilder builder) {
|
||||
// We create a catch-all command that intercepts the first argument, tries to parse it as
|
||||
// the context parameter, then runs the command service AGAIN with that given in a wrapped
|
||||
// context, with the context argument removed so it delegates to the subcommand executor
|
||||
builder.AddCommand("", async (ctx, param, services, info) => {
|
||||
var pkCtx = ctx as PKCommandContext;
|
||||
var res = await ReadContextParameterAsync(param[0] as string);
|
||||
pkCtx.SetContextEntity(res);
|
||||
|
||||
await commandService.ExecuteAsync(pkCtx, Prefix + " " + param[1] as string, services);
|
||||
}, (cb) => {
|
||||
cb.WithPriority(-9999);
|
||||
cb.AddPrecondition(new ContextParameterFallbackPreconditionAttribute());
|
||||
cb.AddParameter<string>("contextValue", (pb) => pb.WithDefault(""));
|
||||
cb.AddParameter<string>("rest", (pb) => pb.WithDefault("").WithIsRemainder(true));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public class ContextParameterFallbackPreconditionAttribute : PreconditionAttribute
|
||||
{
|
||||
public ContextParameterFallbackPreconditionAttribute()
|
||||
{
|
||||
}
|
||||
|
||||
public override async Task<PreconditionResult> CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services)
|
||||
{
|
||||
if (context.GetType().Name != "ContextualContext`1") {
|
||||
return PreconditionResult.FromSuccess();
|
||||
} else {
|
||||
return PreconditionResult.FromError("");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class PKResult : RuntimeResult
|
||||
{
|
||||
public PKResult(CommandError? error, string reason) : base(error, reason)
|
||||
{
|
||||
}
|
||||
|
||||
public static RuntimeResult Error(string reason) => new PKResult(CommandError.Unsuccessful, reason);
|
||||
public static RuntimeResult Success(string reason = null) => new PKResult(null, reason);
|
||||
}
|
||||
}
|
@ -16,10 +16,14 @@ namespace PluralKit {
|
||||
|
||||
public async Task<PKSystem> Create(string systemName = null) {
|
||||
// TODO: handle HID collision case
|
||||
var hid = HidUtils.GenerateHid();
|
||||
var hid = Utils.GenerateHid();
|
||||
return await conn.QuerySingleAsync<PKSystem>("insert into systems (hid, name) values (@Hid, @Name) returning *", new { Hid = hid, Name = systemName });
|
||||
}
|
||||
|
||||
public async Task Link(PKSystem system, ulong accountId) {
|
||||
await conn.ExecuteAsync("insert into accounts (uid, system) values (@Id, @SystemId)", new { Id = accountId, SystemId = system.Id });
|
||||
}
|
||||
|
||||
public async Task<PKSystem> GetByAccount(ulong accountId) {
|
||||
return await conn.QuerySingleAsync<PKSystem>("select systems.* from systems, accounts where accounts.system = system.id and accounts.uid = @Id", new { Id = accountId });
|
||||
}
|
||||
@ -39,6 +43,11 @@ namespace PluralKit {
|
||||
public async Task Delete(PKSystem system) {
|
||||
await conn.DeleteAsync(system);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ulong>> GetLinkedAccountIds(PKSystem system)
|
||||
{
|
||||
return await conn.QueryAsync<ulong>("select uid from accounts where system = @Id", new { Id = system.Id });
|
||||
}
|
||||
}
|
||||
|
||||
public class MemberStore {
|
||||
@ -50,7 +59,7 @@ namespace PluralKit {
|
||||
|
||||
public async Task<PKMember> Create(PKSystem system, string name) {
|
||||
// TODO: handle collision
|
||||
var hid = HidUtils.GenerateHid();
|
||||
var hid = Utils.GenerateHid();
|
||||
return await conn.QuerySingleAsync("insert into members (hid, system, name) values (@Hid, @SystemId, @Name) returning *", new {
|
||||
Hid = hid,
|
||||
SystemID = system.Id,
|
||||
|
@ -10,146 +10,7 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace PluralKit
|
||||
{
|
||||
class UlongEncodeAsLongHandler : SqlMapper.TypeHandler<ulong>
|
||||
{
|
||||
public override ulong Parse(object value)
|
||||
{
|
||||
// Cast to long to unbox, then to ulong (???)
|
||||
return (ulong)(long)value;
|
||||
}
|
||||
|
||||
public override void SetValue(IDbDataParameter parameter, ulong value)
|
||||
{
|
||||
parameter.Value = (long)value;
|
||||
}
|
||||
}
|
||||
|
||||
class PKSystemTypeReader : TypeReader
|
||||
{
|
||||
public override async Task<TypeReaderResult> ReadAsync(ICommandContext context, string input, IServiceProvider services)
|
||||
{
|
||||
var client = services.GetService<IDiscordClient>();
|
||||
var conn = services.GetService<IDbConnection>();
|
||||
|
||||
// System references can take three forms:
|
||||
// - The direct user ID of an account connected to the system
|
||||
// - A @mention of an account connected to the system (<@uid>)
|
||||
// - A system hid
|
||||
|
||||
// First, try direct user ID parsing
|
||||
if (ulong.TryParse(input, out var idFromNumber)) return await FindSystemByAccountHelper(idFromNumber, client, conn);
|
||||
|
||||
// Then, try mention parsing.
|
||||
if (MentionUtils.TryParseUser(input, out var idFromMention)) return await FindSystemByAccountHelper(idFromMention, client, conn);
|
||||
|
||||
// Finally, try HID parsing
|
||||
var res = await conn.QuerySingleOrDefaultAsync<PKSystem>("select * from systems where hid = @Hid", new { Hid = input });
|
||||
if (res != null) return TypeReaderResult.FromSuccess(res);
|
||||
return TypeReaderResult.FromError(CommandError.ObjectNotFound, $"System with ID `${input}` not found.");
|
||||
}
|
||||
|
||||
async Task<TypeReaderResult> FindSystemByAccountHelper(ulong id, IDiscordClient client, IDbConnection conn)
|
||||
{
|
||||
var foundByAccountId = await conn.QuerySingleOrDefaultAsync<PKSystem>("select * from accounts, systems where accounts.system = system.id and accounts.id = @Id", new { Id = id });
|
||||
if (foundByAccountId != null) return TypeReaderResult.FromSuccess(foundByAccountId);
|
||||
|
||||
// We didn't find any, so we try to resolve the user ID to find the associated account,
|
||||
// so we can print their username.
|
||||
var user = await client.GetUserAsync(id);
|
||||
|
||||
// Return descriptive errors based on whether we found the user or not.
|
||||
if (user == null) return TypeReaderResult.FromError(CommandError.ObjectNotFound, $"System or account with ID `${id}` not found.");
|
||||
return TypeReaderResult.FromError(CommandError.ObjectNotFound, $"Account **${user.Username}#${user.Discriminator}** not found.");
|
||||
}
|
||||
}
|
||||
|
||||
class PKMemberTypeReader : TypeReader
|
||||
{
|
||||
public override async Task<TypeReaderResult> ReadAsync(ICommandContext context, string input, IServiceProvider services)
|
||||
{
|
||||
var conn = services.GetService(typeof(IDbConnection)) as IDbConnection;
|
||||
|
||||
// If the sender of the command is in a system themselves,
|
||||
// then try searching by the member's name
|
||||
if (context is PKCommandContext ctx && ctx.SenderSystem != null)
|
||||
{
|
||||
var foundByName = await conn.QuerySingleOrDefaultAsync<PKMember>("select * from members where system = @System and lower(name) = lower(@Name)", new { System = ctx.SenderSystem.Id, Name = input });
|
||||
if (foundByName != null) return TypeReaderResult.FromSuccess(foundByName);
|
||||
}
|
||||
|
||||
// Otherwise, if sender isn't in a system, or no member found by that name,
|
||||
// do a standard by-hid search.
|
||||
var foundByHid = await conn.QuerySingleOrDefaultAsync<PKMember>("select * from members where hid = @Hid", new { Hid = input });
|
||||
if (foundByHid != null) return TypeReaderResult.FromSuccess(foundByHid);
|
||||
return TypeReaderResult.FromError(CommandError.ObjectNotFound, "Member not found.");
|
||||
}
|
||||
}
|
||||
|
||||
/// Subclass of ICommandContext with PK-specific additional fields and functionality
|
||||
public class PKCommandContext : SocketCommandContext, ICommandContext
|
||||
{
|
||||
public IDbConnection Connection { get; }
|
||||
public PKSystem SenderSystem { get; }
|
||||
|
||||
public PKCommandContext(DiscordSocketClient client, SocketUserMessage msg, IDbConnection connection, PKSystem system) : base(client, msg)
|
||||
{
|
||||
Connection = connection;
|
||||
SenderSystem = system;
|
||||
}
|
||||
}
|
||||
|
||||
public class ContextualContext<T> : PKCommandContext
|
||||
{
|
||||
public T ContextEntity { get; internal set; }
|
||||
|
||||
public ContextualContext(PKCommandContext ctx, T contextEntity): base(ctx.Client, ctx.Message, ctx.Connection, ctx.SenderSystem)
|
||||
{
|
||||
this.ContextEntity = contextEntity;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class ContextParameterModuleBase<T> : ModuleBase<ContextualContext<T>>
|
||||
{
|
||||
public IServiceProvider _services { get; set; }
|
||||
public CommandService _commands { get; set; }
|
||||
|
||||
public abstract string Prefix { get; }
|
||||
public abstract Task<T> ReadContextParameterAsync(string value);
|
||||
|
||||
protected override void OnModuleBuilding(CommandService commandService, ModuleBuilder builder) {
|
||||
// We create a catch-all command that intercepts the first argument, tries to parse it as
|
||||
// the context parameter, then runs the command service AGAIN with that given in a wrapped
|
||||
// context, with the context argument removed so it delegates to the subcommand executor
|
||||
builder.AddCommand("", async (ctx, param, services, info) => {
|
||||
var pkCtx = ctx as PKCommandContext;
|
||||
var res = await ReadContextParameterAsync(param[0] as string);
|
||||
await commandService.ExecuteAsync(new ContextualContext<T>(pkCtx, res), Prefix + " " + param[1] as string, services);
|
||||
}, (cb) => {
|
||||
cb.WithPriority(-9999);
|
||||
cb.AddPrecondition(new ContextParameterFallbackPreconditionAttribute());
|
||||
cb.AddParameter<string>("contextValue", (pb) => pb.WithDefault(""));
|
||||
cb.AddParameter<string>("rest", (pb) => pb.WithDefault("").WithIsRemainder(true));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public class ContextParameterFallbackPreconditionAttribute : PreconditionAttribute
|
||||
{
|
||||
public ContextParameterFallbackPreconditionAttribute()
|
||||
{
|
||||
}
|
||||
|
||||
public override async Task<PreconditionResult> CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services)
|
||||
{
|
||||
if (context.GetType().Name != "ContextualContext`1") {
|
||||
return PreconditionResult.FromSuccess();
|
||||
} else {
|
||||
return PreconditionResult.FromError("");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class HidUtils
|
||||
public static class Utils
|
||||
{
|
||||
public static string GenerateHid()
|
||||
{
|
||||
@ -162,15 +23,10 @@ namespace PluralKit
|
||||
}
|
||||
return hid;
|
||||
}
|
||||
}
|
||||
|
||||
public class PKResult : RuntimeResult
|
||||
{
|
||||
public PKResult(CommandError? error, string reason) : base(error, reason)
|
||||
{
|
||||
}
|
||||
|
||||
public static RuntimeResult Error(string reason) => new PKResult(CommandError.Unsuccessful, reason);
|
||||
public static RuntimeResult Success(string reason = null) => new PKResult(null, reason);
|
||||
public static string Truncate(this string str, int maxLength, string ellipsis = "...") {
|
||||
if (str.Length < maxLength) return str;
|
||||
return str.Substring(0, maxLength - ellipsis.Length) + ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user