From 2206185d55f001f8bd53f43e9fb24b1692bef222 Mon Sep 17 00:00:00 2001 From: Ske Date: Tue, 25 Aug 2020 19:32:19 +0200 Subject: [PATCH] Add command prefix configuration --- PluralKit.Bot/BotConfig.cs | 7 ++++ PluralKit.Bot/Handlers/MessageCreated.cs | 47 +++++++++++++++--------- README.md | 1 + 3 files changed, 38 insertions(+), 17 deletions(-) diff --git a/PluralKit.Bot/BotConfig.cs b/PluralKit.Bot/BotConfig.cs index d941fc70..153b965c 100644 --- a/PluralKit.Bot/BotConfig.cs +++ b/PluralKit.Bot/BotConfig.cs @@ -2,7 +2,14 @@ namespace PluralKit.Bot { public class BotConfig { + public static readonly string[] DefaultPrefixes = {"pk;", "pk!"}; + public string Token { get; set; } public ulong? ClientId { get; set; } + + // ASP.NET configuration merges arrays with defaults, so we leave this field nullable + // and fall back to the separate default array at the use site :) + // This does bind [] as null (therefore default) instead of an empty array, but I can live w/ that. + public string[] Prefixes { get; set; } } } \ No newline at end of file diff --git a/PluralKit.Bot/Handlers/MessageCreated.cs b/PluralKit.Bot/Handlers/MessageCreated.cs index 9fbf18ed..64ece7fe 100644 --- a/PluralKit.Bot/Handlers/MessageCreated.cs +++ b/PluralKit.Bot/Handlers/MessageCreated.cs @@ -23,11 +23,11 @@ namespace PluralKit.Bot private readonly ProxyService _proxy; private readonly ILifetimeScope _services; private readonly IDatabase _db; - private readonly IDataStore _data; + private readonly BotConfig _config; public MessageCreated(LastMessageCacheService lastMessageCache, LoggerCleanService loggerClean, IMetrics metrics, ProxyService proxy, DiscordShardedClient client, - CommandTree tree, ILifetimeScope services, IDatabase db, IDataStore data) + CommandTree tree, ILifetimeScope services, IDatabase db, BotConfig config) { _lastMessageCache = lastMessageCache; _loggerClean = loggerClean; @@ -37,7 +37,7 @@ namespace PluralKit.Bot _tree = tree; _services = services; _db = db; - _data = data; + _config = config; } public DiscordChannel ErrorChannelFor(MessageCreateEventArgs evt) => evt.Channel; @@ -87,26 +87,19 @@ namespace PluralKit.Bot var content = evt.Message.Content; if (content == null) return false; - var argPos = -1; - // Check if message starts with the command prefix - if (content.StartsWith("pk;", StringComparison.InvariantCultureIgnoreCase)) argPos = 3; - else if (content.StartsWith("pk!", StringComparison.InvariantCultureIgnoreCase)) argPos = 3; - else if (DiscordUtils.HasMentionPrefix(content, ref argPos, out var id)) // Set argPos to the proper value - if (id != _client.CurrentUser.Id) // But undo it if it's someone else's ping - argPos = -1; + // Check for command prefix + if (!HasCommandPrefix(content, out var cmdStart)) + return false; - // If we didn't find a prefix, give up handling commands - if (argPos == -1) return false; - - // Trim leading whitespace from command without actually modifying the wring + // Trim leading whitespace from command without actually modifying the string // This just moves the argPos pointer by however much whitespace is at the start of the post-argPos string - var trimStartLengthDiff = content.Substring(argPos).Length - content.Substring(argPos).TrimStart().Length; - argPos += trimStartLengthDiff; + var trimStartLengthDiff = content.Substring(cmdStart).Length - content.Substring(cmdStart).TrimStart().Length; + cmdStart += trimStartLengthDiff; try { var system = ctx.SystemId != null ? await _db.Execute(c => c.QuerySystem(ctx.SystemId.Value)) : null; - await _tree.ExecuteCommand(new Context(_services, evt.Client, evt.Message, argPos, system, ctx)); + await _tree.ExecuteCommand(new Context(_services, evt.Client, evt.Message, cmdStart, system, ctx)); } catch (PKError) { @@ -117,6 +110,26 @@ namespace PluralKit.Bot return true; } + private bool HasCommandPrefix(string message, out int argPos) + { + // First, try prefixes defined in the config + var prefixes = _config.Prefixes ?? BotConfig.DefaultPrefixes; + foreach (var prefix in prefixes) + { + if (!message.StartsWith(prefix, StringComparison.InvariantCultureIgnoreCase)) continue; + + argPos = prefix.Length; + return true; + } + + // Then, check mention prefix (must be the bot user, ofc) + argPos = -1; + if (DiscordUtils.HasMentionPrefix(message, ref argPos, out var id)) + return id == _client.CurrentUser.Id; + + return false; + } + private async ValueTask TryHandleProxy(MessageCreateEventArgs evt, MessageContext ctx) { try diff --git a/README.md b/README.md index 1c750a17..a6ca8b28 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ The configuration file needs to be placed in the bot's working directory (usuall The configuration file is in JSON format (albeit with a `.conf` extension). The following keys are available (using `.` to indicate a nested object level), bolded key names are required: * **`PluralKit.Bot.Token`**: the Discord bot token to connect with * **`PluralKit.Database`**: the URI of the database to connect to (in [ADO.NET Npgsql format](https://www.connectionstrings.com/npgsql/)) +* `PluralKit.Bot.Prefixes`: an array of command prefixes to use (default `["pk;", "pk!"]`). * `PluralKit.Bot.ClientId` *(optional)*: the ID of the bot's user account, used when generating invite links through `pk;invite`. It's automatically determined if not present, but overriding it may be useful for private instances that still want a public invite link. * `PluralKit.SentryUrl` *(optional)*: the [Sentry](https://sentry.io/welcome/) client key/DSN to report runtime errors to. If absent, disables Sentry integration. * `PluralKit.InfluxUrl` *(optional)*: the URL to an [InfluxDB](https://www.influxdata.com/products/influxdb-overview/) server to report aggregate statistics to. An example of these stats can be seen on [the public stats page](https://stats.pluralkit.me).