Add command prefix configuration
This commit is contained in:
		| @@ -2,7 +2,14 @@ namespace PluralKit.Bot | |||||||
| { | { | ||||||
|     public class BotConfig |     public class BotConfig | ||||||
|     { |     { | ||||||
|  |         public static readonly string[] DefaultPrefixes = {"pk;", "pk!"}; | ||||||
|  |  | ||||||
|         public string Token { get; set; } |         public string Token { get; set; } | ||||||
|         public ulong? ClientId { 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; } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -23,11 +23,11 @@ namespace PluralKit.Bot | |||||||
|         private readonly ProxyService _proxy; |         private readonly ProxyService _proxy; | ||||||
|         private readonly ILifetimeScope _services; |         private readonly ILifetimeScope _services; | ||||||
|         private readonly IDatabase _db; |         private readonly IDatabase _db; | ||||||
|         private readonly IDataStore _data; |         private readonly BotConfig _config; | ||||||
|  |  | ||||||
|         public MessageCreated(LastMessageCacheService lastMessageCache, LoggerCleanService loggerClean, |         public MessageCreated(LastMessageCacheService lastMessageCache, LoggerCleanService loggerClean, | ||||||
|                               IMetrics metrics, ProxyService proxy, DiscordShardedClient client, |                               IMetrics metrics, ProxyService proxy, DiscordShardedClient client, | ||||||
|                               CommandTree tree, ILifetimeScope services, IDatabase db, IDataStore data) |                               CommandTree tree, ILifetimeScope services, IDatabase db, BotConfig config) | ||||||
|         { |         { | ||||||
|             _lastMessageCache = lastMessageCache; |             _lastMessageCache = lastMessageCache; | ||||||
|             _loggerClean = loggerClean; |             _loggerClean = loggerClean; | ||||||
| @@ -37,7 +37,7 @@ namespace PluralKit.Bot | |||||||
|             _tree = tree; |             _tree = tree; | ||||||
|             _services = services; |             _services = services; | ||||||
|             _db = db; |             _db = db; | ||||||
|             _data = data; |             _config = config; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public DiscordChannel ErrorChannelFor(MessageCreateEventArgs evt) => evt.Channel; |         public DiscordChannel ErrorChannelFor(MessageCreateEventArgs evt) => evt.Channel; | ||||||
| @@ -87,26 +87,19 @@ namespace PluralKit.Bot | |||||||
|             var content = evt.Message.Content; |             var content = evt.Message.Content; | ||||||
|             if (content == null) return false; |             if (content == null) return false; | ||||||
|  |  | ||||||
|             var argPos = -1; |             // Check for command prefix | ||||||
|             // Check if message starts with the command prefix |             if (!HasCommandPrefix(content, out var cmdStart)) | ||||||
|             if (content.StartsWith("pk;", StringComparison.InvariantCultureIgnoreCase)) argPos = 3; |                 return false; | ||||||
|             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; |  | ||||||
|  |  | ||||||
|             // If we didn't find a prefix, give up handling commands |             // Trim leading whitespace from command without actually modifying the string | ||||||
|             if (argPos == -1) return false; |  | ||||||
|  |  | ||||||
|             // Trim leading whitespace from command without actually modifying the wring |  | ||||||
|             // This just moves the argPos pointer by however much whitespace is at the start of the post-argPos 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; |             var trimStartLengthDiff = content.Substring(cmdStart).Length - content.Substring(cmdStart).TrimStart().Length; | ||||||
|             argPos += trimStartLengthDiff; |             cmdStart += trimStartLengthDiff; | ||||||
|  |  | ||||||
|             try |             try | ||||||
|             { |             { | ||||||
|                 var system = ctx.SystemId != null ? await _db.Execute(c => c.QuerySystem(ctx.SystemId.Value)) : null; |                 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) |             catch (PKError) | ||||||
|             { |             { | ||||||
| @@ -117,6 +110,26 @@ namespace PluralKit.Bot | |||||||
|             return true; |             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<bool> TryHandleProxy(MessageCreateEventArgs evt, MessageContext ctx) |         private async ValueTask<bool> TryHandleProxy(MessageCreateEventArgs evt, MessageContext ctx) | ||||||
|         { |         { | ||||||
|             try |             try | ||||||
|   | |||||||
| @@ -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: | 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.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.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.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.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).  | * `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).  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user