Update default example configuration file

This commit is contained in:
Noi 2022-07-28 14:08:23 -07:00
parent 8317f09b04
commit 52d3b1b9d4
3 changed files with 92 additions and 18 deletions

View file

@ -1,3 +1,45 @@
{ {
// To do: Write good example config and decent documentation to go along with it. "$schema": "https://raw.githubusercontent.com/NoiTheCat/RegexBot/main/ServerConfigSchema.json",
"Name": "SERVER NAME", // Server name is optional, but useful as a reference
"Moderators": [
// Users and roles are accepted here.
"MODERATOR"
],
/*
The following configuration is provided as a sample for commonly-used features.
For a detailed reference which includes all possible configuration settings, see:
(TODO put documentation link here)
*/
"RegexModerator": [
{
"Label": "No cheese",
"Regex": "cheese",
"Response": [
"say #_ You can't say that, that's illegal",
"delete"
]
},
{
"Label": "Secret club initiation",
"Regex": "my name is .* and I dislike cheese",
"Response": [
"say @_ We welcome you.",
"addrole &00000::Secret Club member"
]
}
],
"AutoResponder": [
{
"Label": "Infinite no u",
"Regex": "no u",
"Reply": "no u"
},
{
"Label": "Acknowledge praise",
"Regex": "yes u",
"Reply": ":blush:"
}
]
} }

21
ServerConfigSchema.json Normal file
View file

@ -0,0 +1,21 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"Name": {
"type": "string",
"description": "The server's name. The value is unused by the application and is only for user reference."
},
"Moderators": {
"type": "array",
"description": "A list of entities which the bot should recognize as moderators.",
"items": {
"type": "string"
}
}
/* schema still a work in progress */
},
"required": [
"Moderators"
]
}

View file

@ -22,7 +22,7 @@ class ModuleStateService : Service {
} }
private async Task RefreshGuildState(SocketGuild arg) { private async Task RefreshGuildState(SocketGuild arg) {
if (await ProcessConfiguration(arg.Id)) Log($"Configuration refreshed for server {arg.Id}."); if (await ProcessConfiguration(arg)) Log($"Configuration refreshed for '{arg.Name}'.");
} }
private Task RemoveGuildData(SocketGuild arg) { private Task RemoveGuildData(SocketGuild arg) {
@ -60,8 +60,8 @@ class ModuleStateService : Service {
/// This takes an all-or-nothing approach. Should there be a single issue in processing /// This takes an all-or-nothing approach. Should there be a single issue in processing
/// configuration, all existing state data is kept. /// configuration, all existing state data is kept.
/// </remarks> /// </remarks>
private async Task<bool> ProcessConfiguration(ulong guildId) { private async Task<bool> ProcessConfiguration(SocketGuild guild) {
var jstr = await LoadConfigFile(guildId); var jstr = await LoadConfigFile(guild);
JObject guildConf; JObject guildConf;
try { try {
var tok = JToken.Parse(jstr); var tok = JToken.Parse(jstr);
@ -71,7 +71,7 @@ class ModuleStateService : Service {
throw new InvalidCastException("Configuration is not valid JSON."); throw new InvalidCastException("Configuration is not valid JSON.");
} }
} catch (Exception ex) when (ex is JsonReaderException or InvalidCastException) { } catch (Exception ex) when (ex is JsonReaderException or InvalidCastException) {
Log($"Error loading configuration for server ID {guildId}: {ex.Message}"); Log($"Error loading configuration for server ID {guild.Id}: {ex.Message}");
return false; return false;
} }
@ -83,40 +83,51 @@ class ModuleStateService : Service {
foreach (var module in BotClient.Modules) { foreach (var module in BotClient.Modules) {
var t = module.GetType(); var t = module.GetType();
try { try {
var state = await module.CreateGuildStateAsync(guildId, guildConf[module.Name]!); var state = await module.CreateGuildStateAsync(guild.Id, guildConf[module.Name]!);
newStates.Add(t, state); newStates.Add(t, state);
} catch (ModuleLoadException ex) { } catch (ModuleLoadException ex) {
Log($"{guildId}: Error reading configuration regarding {module.Name}: {ex.Message}"); Log($"{guild.Id}: Error reading configuration regarding {module.Name}: {ex.Message}");
return false; return false;
} catch (Exception ex) when (ex is not ModuleLoadException) { } catch (Exception ex) when (ex is not ModuleLoadException) {
Log("Unhandled exception while initializing guild state for module:\n" + Log("Unhandled exception while initializing guild state for module:\n" +
$"Module: {module.Name} | " + $"Module: {module.Name} | " +
$"Guild: {guildId} ({BotClient.DiscordClient.GetGuild(guildId)?.Name ?? "unknown name"})\n" + $"Guild: {guild.Id} ({BotClient.DiscordClient.GetGuild(guild.Id)?.Name ?? "unknown name"})\n" +
$"```\n{ex}\n```"); $"```\n{ex}\n```");
return false; return false;
} }
} }
lock (_storageLock) { lock (_storageLock) {
_moderators[guildId] = mods; _moderators[guild.Id] = mods;
_stateData[guildId] = newStates; _stateData[guild.Id] = newStates;
} }
return true; return true;
} }
private async Task<string> LoadConfigFile(ulong guildId) { private async Task<string> LoadConfigFile(SocketGuild guild) {
// Per-guild configuration exists under `config/(guild ID).json` // Per-guild configuration exists under `config/(guild ID).json`
var basePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly()!.Location) + var basePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly()!.Location) +
Path.DirectorySeparatorChar + "config" + Path.DirectorySeparatorChar; Path.DirectorySeparatorChar + "config" + Path.DirectorySeparatorChar;
if (!Directory.Exists(basePath)) Directory.CreateDirectory(basePath); if (!Directory.Exists(basePath)) Directory.CreateDirectory(basePath);
var path = basePath + guildId + ".json"; var path = basePath + guild.Id + ".json";
if (File.Exists(path)) { if (File.Exists(path)) {
return await File.ReadAllTextAsync(path); return await File.ReadAllTextAsync(path);
} else { } else { // Write default configuration to new file
// Write default configuration to new file string fileContents;
using var resStream = Assembly.GetExecutingAssembly().GetManifestResourceStream($"{nameof(RegexBot)}.DefaultGuildConfig.json"); using (var resStream = Assembly.GetExecutingAssembly()
using (var newFile = File.OpenWrite(path)) resStream!.CopyTo(newFile); .GetManifestResourceStream($"{nameof(RegexBot)}.DefaultGuildConfig.json")!) {
Log($"Created initial configuration file in config{Path.DirectorySeparatorChar}{guildId}.json"); using var readin = new StreamReader(resStream, encoding: System.Text.Encoding.UTF8);
return await LoadConfigFile(guildId); fileContents = readin.ReadToEnd();
}
var userex = BotClient.DiscordClient.CurrentUser;
fileContents = fileContents.Replace("SERVER NAME", guild.Name).Replace("MODERATOR", $"@{userex.Id}::{userex.Username}");
using (var newFile = File.OpenWrite(path)) {
var w = new StreamWriter(newFile);
w.Write(fileContents);
w.Flush();
w.Close();
}
Log($"Created initial configuration file in config{Path.DirectorySeparatorChar}{guild.Id}.json");
return await LoadConfigFile(guild);
} }
} }
} }