Merge pull request #4 from NoiTheCat/consolidate-config
Consolidate bot and guild configuration into one file
This commit is contained in:
commit
495636baec
12 changed files with 154 additions and 169 deletions
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
|
@ -11,7 +11,7 @@
|
||||||
"preLaunchTask": "build",
|
"preLaunchTask": "build",
|
||||||
// If you have changed target frameworks, make sure to update the program path.
|
// If you have changed target frameworks, make sure to update the program path.
|
||||||
"program": "${workspaceFolder}/bin/Debug/net6.0/RegexBot.dll",
|
"program": "${workspaceFolder}/bin/Debug/net6.0/RegexBot.dll",
|
||||||
"args": [],
|
"args": [ "-c", "${workspaceFolder}/bin/Debug/net6.0/config.json" ],
|
||||||
"cwd": "${workspaceFolder}",
|
"cwd": "${workspaceFolder}",
|
||||||
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
|
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
|
||||||
"console": "internalConsole",
|
"console": "internalConsole",
|
||||||
|
|
|
@ -1,14 +1,9 @@
|
||||||
using CommandLine;
|
using CommandLine;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace RegexBot;
|
namespace RegexBot;
|
||||||
/// <summary>
|
class Configuration {
|
||||||
/// Contains essential instance configuration for this bot including Discord connection settings, service configuration,
|
|
||||||
/// and command-line options.
|
|
||||||
/// </summary>
|
|
||||||
class InstanceConfig {
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Token used for Discord authentication.
|
/// Token used for Discord authentication.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -19,18 +14,20 @@ class InstanceConfig {
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal IReadOnlyList<string> Assemblies { get; }
|
internal IReadOnlyList<string> Assemblies { get; }
|
||||||
|
|
||||||
public string? SqlHost { get; }
|
public JObject ServerConfigs { get; }
|
||||||
public string? SqlDatabase { get; }
|
|
||||||
public string SqlUsername { get; }
|
// SQL properties:
|
||||||
public string SqlPassword { get; }
|
public string? Host { get; }
|
||||||
|
public string? Database { get; }
|
||||||
|
public string Username { get; }
|
||||||
|
public string Password { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets up instance configuration object from file and command line parameters.
|
/// Sets up instance configuration object from file and command line parameters.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal InstanceConfig() {
|
internal Configuration() {
|
||||||
var args = CommandLineParameters.Parse(Environment.GetCommandLineArgs());
|
var args = CommandLineParameters.Parse(Environment.GetCommandLineArgs());
|
||||||
var path = args?.ConfigFile ?? Path.GetDirectoryName(Assembly.GetEntryAssembly()!.Location)
|
var path = args?.ConfigFile!;
|
||||||
+ Path.DirectorySeparatorChar + "." + Path.DirectorySeparatorChar + "instance.json";
|
|
||||||
|
|
||||||
JObject conf;
|
JObject conf;
|
||||||
try {
|
try {
|
||||||
|
@ -54,10 +51,16 @@ class InstanceConfig {
|
||||||
throw new Exception($"'{nameof(Assemblies)}' is not properly specified in configuration.");
|
throw new Exception($"'{nameof(Assemblies)}' is not properly specified in configuration.");
|
||||||
}
|
}
|
||||||
|
|
||||||
SqlHost = ReadConfKey<string>(conf, nameof(SqlHost), false);
|
var dbconf = conf["DatabaseOptions"]?.Value<JObject>();
|
||||||
SqlDatabase = ReadConfKey<string?>(conf, nameof(SqlDatabase), false);
|
if (dbconf == null) throw new Exception("Database settings were not specified in configuration.");
|
||||||
SqlUsername = ReadConfKey<string>(conf, nameof(SqlUsername), true);
|
// TODO more detailed database configuration? password file, other advanced authentication settings... look into this.
|
||||||
SqlPassword = ReadConfKey<string>(conf, nameof(SqlPassword), true);
|
Host = ReadConfKey<string>(dbconf, nameof(Host), false);
|
||||||
|
Database = ReadConfKey<string?>(dbconf, nameof(Database), false);
|
||||||
|
Username = ReadConfKey<string>(dbconf, nameof(Username), true);
|
||||||
|
Password = ReadConfKey<string>(dbconf, nameof(Password), true);
|
||||||
|
|
||||||
|
ServerConfigs = conf["Servers"]?.Value<JObject>();
|
||||||
|
if (ServerConfigs == null) throw new Exception("No server configurations were specified.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static T? ReadConfKey<T>(JObject jc, string key, [DoesNotReturnIf(true)] bool failOnEmpty) {
|
private static T? ReadConfKey<T>(JObject jc, string key, [DoesNotReturnIf(true)] bool failOnEmpty) {
|
||||||
|
@ -67,8 +70,8 @@ class InstanceConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
class CommandLineParameters {
|
class CommandLineParameters {
|
||||||
[Option('c', "config", Default = null)]
|
[Option('c', "config", Default = "config.json")]
|
||||||
public string? ConfigFile { get; set; } = null!;
|
public string? ConfigFile { get; set; } = null;
|
||||||
|
|
||||||
public static CommandLineParameters? Parse(string[] args) {
|
public static CommandLineParameters? Parse(string[] args) {
|
||||||
CommandLineParameters? result = null;
|
CommandLineParameters? result = null;
|
44
ConfigurationSchema.json
Normal file
44
ConfigurationSchema.json
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"BotToken": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The token used by the bot to connect to Discord."
|
||||||
|
},
|
||||||
|
"Assemblies": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "A list of additional files to be loaded to extend the bot's functionality.",
|
||||||
|
"default": [ "RegexBot.dll" ]
|
||||||
|
},
|
||||||
|
"DatabaseOptions": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "A set of options for the SQL database connection.",
|
||||||
|
"properties": {
|
||||||
|
"Host": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The SQL host, whether a hostname, IP address, or path to a socket."
|
||||||
|
},
|
||||||
|
"Database": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The target SQL database name to connect to, if different from the default."
|
||||||
|
},
|
||||||
|
"Username": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The username used for SQL server authentication."
|
||||||
|
},
|
||||||
|
"Password": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The password used for SQL server authentication."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [ "Username", "Password" ]
|
||||||
|
},
|
||||||
|
"Servers": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "A collection of server configurations with keys representing server IDs and values containing the respective server's configuration."
|
||||||
|
/* TODO unfinished */
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [ "BotToken", "DatabaseOptions", "Servers" ]
|
||||||
|
}
|
|
@ -10,15 +10,16 @@ public class BotDatabaseContext : DbContext {
|
||||||
|
|
||||||
static BotDatabaseContext() {
|
static BotDatabaseContext() {
|
||||||
// Get our own config loaded just for the SQL stuff
|
// Get our own config loaded just for the SQL stuff
|
||||||
var conf = new InstanceConfig();
|
// TODO this should probably be cached, or otherwise loaded in a better way
|
||||||
|
var conf = new Configuration();
|
||||||
_connectionString = new NpgsqlConnectionStringBuilder() {
|
_connectionString = new NpgsqlConnectionStringBuilder() {
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
IncludeErrorDetail = true,
|
IncludeErrorDetail = true,
|
||||||
#endif
|
#endif
|
||||||
Host = conf.SqlHost ?? "localhost", // default to localhost
|
Host = conf.Host ?? "localhost", // default to localhost
|
||||||
Database = conf.SqlDatabase,
|
Database = conf.Database,
|
||||||
Username = conf.SqlUsername,
|
Username = conf.Username,
|
||||||
Password = conf.SqlPassword
|
Password = conf.Password
|
||||||
}.ToString();
|
}.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
{
|
|
||||||
"$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:"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -7,7 +7,7 @@ static class ModuleLoader {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Given the instance configuration, loads all appropriate types from file specified in it.
|
/// Given the instance configuration, loads all appropriate types from file specified in it.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static ReadOnlyCollection<RegexbotModule> Load(InstanceConfig conf, RegexbotClient rb) {
|
internal static ReadOnlyCollection<RegexbotModule> Load(Configuration conf, RegexbotClient rb) {
|
||||||
var path = Path.GetDirectoryName(Assembly.GetEntryAssembly()!.Location) + Path.DirectorySeparatorChar;
|
var path = Path.GetDirectoryName(Assembly.GetEntryAssembly()!.Location) + Path.DirectorySeparatorChar;
|
||||||
var modules = new List<RegexbotModule>();
|
var modules = new List<RegexbotModule>();
|
||||||
|
|
||||||
|
|
|
@ -15,9 +15,9 @@ class Program {
|
||||||
StartTime = DateTimeOffset.UtcNow;
|
StartTime = DateTimeOffset.UtcNow;
|
||||||
Console.WriteLine("Bot start time: " + StartTime.ToString("u"));
|
Console.WriteLine("Bot start time: " + StartTime.ToString("u"));
|
||||||
|
|
||||||
InstanceConfig cfg;
|
Configuration cfg;
|
||||||
try {
|
try {
|
||||||
cfg = new InstanceConfig(); // Program may exit within here.
|
cfg = new Configuration(); // Program may exit within here.
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Console.WriteLine(ex.Message);
|
Console.WriteLine(ex.Message);
|
||||||
Environment.ExitCode = 1;
|
Environment.ExitCode = 1;
|
||||||
|
|
|
@ -11,14 +11,6 @@
|
||||||
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<None Remove="DefaultGuildConfig.json" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<EmbeddedResource Include="DefaultGuildConfig.json" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="CommandLineParser" Version="2.9.1" />
|
<PackageReference Include="CommandLineParser" Version="2.9.1" />
|
||||||
<PackageReference Include="Discord.Net" Version="3.8.1" />
|
<PackageReference Include="Discord.Net" Version="3.8.1" />
|
||||||
|
|
|
@ -8,7 +8,7 @@ public partial class RegexbotClient {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets application instance configuration.
|
/// Gets application instance configuration.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal InstanceConfig Config { get; }
|
internal Configuration Config { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the Discord client instance.
|
/// Gets the Discord client instance.
|
||||||
|
@ -20,14 +20,14 @@ public partial class RegexbotClient {
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal IReadOnlyCollection<RegexbotModule> Modules { get; }
|
internal IReadOnlyCollection<RegexbotModule> Modules { get; }
|
||||||
|
|
||||||
internal RegexbotClient(InstanceConfig conf, DiscordSocketClient client) {
|
internal RegexbotClient(Configuration conf, DiscordSocketClient client) {
|
||||||
Config = conf;
|
Config = conf;
|
||||||
DiscordClient = client;
|
DiscordClient = client;
|
||||||
|
|
||||||
// Get all services started up
|
// Get all services started up
|
||||||
_svcLogging = new Services.Logging.LoggingService(this);
|
_svcLogging = new Services.Logging.LoggingService(this);
|
||||||
_svcSharedEvents = new Services.SharedEventService.SharedEventService(this);
|
_svcSharedEvents = new Services.SharedEventService.SharedEventService(this);
|
||||||
_svcGuildState = new Services.ModuleState.ModuleStateService(this);
|
_svcGuildState = new Services.ModuleState.ModuleStateService(this, conf.ServerConfigs);
|
||||||
_svcCommonFunctions = new Services.CommonFunctions.CommonFunctionsService(this);
|
_svcCommonFunctions = new Services.CommonFunctions.CommonFunctionsService(this);
|
||||||
_svcEntityCache = new Services.EntityCache.EntityCacheService(this);
|
_svcEntityCache = new Services.EntityCache.EntityCacheService(this);
|
||||||
|
|
||||||
|
|
57
SampleConfiguration.json
Normal file
57
SampleConfiguration.json
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://raw.githubusercontent.com/NoiTheCat/RegexBot/main/ConfigurationSchema.json",
|
||||||
|
|
||||||
|
"BotToken": "12345678901234567890qwertyuiop.1234567890",
|
||||||
|
//"Assemblies": [ "RegexBot.dll" ],
|
||||||
|
"DatabaseOptions": {
|
||||||
|
"Username": "regexbot",
|
||||||
|
"Password": "regexbot"
|
||||||
|
},
|
||||||
|
|
||||||
|
"Servers": {
|
||||||
|
"00000000": { // Place server ID here
|
||||||
|
"Name": "SERVER NAME", // Server name is unused by the bot, but is 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
|
||||||
|
this project's documentation.
|
||||||
|
*/
|
||||||
|
"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 (hate|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:"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,21 +0,0 @@
|
||||||
{
|
|
||||||
"$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"
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -1,20 +1,18 @@
|
||||||
using Newtonsoft.Json;
|
using RegexBot.Common;
|
||||||
using RegexBot.Common;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace RegexBot.Services.ModuleState;
|
namespace RegexBot.Services.ModuleState;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Implements per-module storage and retrieval of guild-specific state data, most typically but not limited to configuration data.
|
/// Implements per-module storage and retrieval of guild-specific state data, most typically but not limited to configuration data.
|
||||||
/// To that end, this service handles loading and validation of per-guild configuration files.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class ModuleStateService : Service {
|
class ModuleStateService : Service {
|
||||||
private readonly object _storageLock = new();
|
|
||||||
private readonly Dictionary<ulong, EntityList> _moderators;
|
private readonly Dictionary<ulong, EntityList> _moderators;
|
||||||
private readonly Dictionary<ulong, Dictionary<Type, object?>> _stateData;
|
private readonly Dictionary<ulong, Dictionary<Type, object?>> _stateData;
|
||||||
|
private readonly JObject _serverConfs;
|
||||||
|
|
||||||
public ModuleStateService(RegexbotClient bot) : base(bot) {
|
public ModuleStateService(RegexbotClient bot, JObject servers) : base(bot) {
|
||||||
_moderators = new();
|
_moderators = new();
|
||||||
_stateData = new();
|
_stateData = new();
|
||||||
|
_serverConfs = servers;
|
||||||
|
|
||||||
bot.DiscordClient.GuildAvailable += RefreshGuildState;
|
bot.DiscordClient.GuildAvailable += RefreshGuildState;
|
||||||
bot.DiscordClient.JoinedGuild += RefreshGuildState;
|
bot.DiscordClient.JoinedGuild += RefreshGuildState;
|
||||||
|
@ -27,45 +25,31 @@ class ModuleStateService : Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task RemoveGuildData(SocketGuild arg) {
|
private Task RemoveGuildData(SocketGuild arg) {
|
||||||
lock (_storageLock) {
|
_stateData.Remove(arg.Id);
|
||||||
_stateData.Remove(arg.Id);
|
_moderators.Remove(arg.Id);
|
||||||
_moderators.Remove(arg.Id);
|
|
||||||
}
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hooked
|
// Hooked
|
||||||
public T? DoGetStateObj<T>(ulong guildId, Type t) {
|
public T? DoGetStateObj<T>(ulong guildId, Type t) {
|
||||||
lock (_storageLock) {
|
if (_stateData.ContainsKey(guildId) && _stateData[guildId].ContainsKey(t)) {
|
||||||
if (_stateData.ContainsKey(guildId) && _stateData[guildId].ContainsKey(t)) {
|
// Leave handling of potential InvalidCastException to caller.
|
||||||
// Leave handling of potential InvalidCastException to caller.
|
return (T?)_stateData[guildId][t];
|
||||||
return (T?)_stateData[guildId][t];
|
|
||||||
}
|
|
||||||
return default;
|
|
||||||
}
|
}
|
||||||
|
return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hooked
|
// Hooked
|
||||||
public EntityList DoGetModlist(ulong guildId) {
|
public EntityList DoGetModlist(ulong guildId) {
|
||||||
lock (_storageLock) {
|
if (_moderators.TryGetValue(guildId, out var mods)) return mods;
|
||||||
if (_moderators.TryGetValue(guildId, out var mods)) return mods;
|
else return new EntityList();
|
||||||
else return new EntityList();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> ProcessConfiguration(SocketGuild guild) {
|
private async Task<bool> ProcessConfiguration(SocketGuild guild) {
|
||||||
var jstr = await LoadConfigFile(guild);
|
var guildConf = _serverConfs[guild.Id.ToString()]?.Value<JObject>();
|
||||||
JObject guildConf;
|
if (guildConf == null) {
|
||||||
try {
|
Log($"{guild.Name} ({guild.Id}) has no configuration. Add config or consider removing bot from server.");
|
||||||
var tok = JToken.Parse(jstr);
|
return true;
|
||||||
if (tok.Type == JTokenType.Object) {
|
|
||||||
guildConf = (JObject)tok;
|
|
||||||
} else {
|
|
||||||
throw new InvalidCastException("Configuration is not valid JSON.");
|
|
||||||
}
|
|
||||||
} catch (Exception ex) when (ex is JsonReaderException or InvalidCastException) {
|
|
||||||
Log($"Error loading configuration for server ID {guild.Id}: {ex.Message}");
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load moderator list
|
// Load moderator list
|
||||||
|
@ -86,38 +70,8 @@ class ModuleStateService : Service {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lock (_storageLock) {
|
_moderators[guild.Id] = mods;
|
||||||
_moderators[guild.Id] = mods;
|
_stateData[guild.Id] = newStates;
|
||||||
_stateData[guild.Id] = newStates;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<string> LoadConfigFile(SocketGuild guild) {
|
|
||||||
// Per-guild configuration exists under `config/(guild ID).json`
|
|
||||||
var basePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly()!.Location) +
|
|
||||||
Path.DirectorySeparatorChar + "config" + Path.DirectorySeparatorChar;
|
|
||||||
if (!Directory.Exists(basePath)) Directory.CreateDirectory(basePath);
|
|
||||||
var path = basePath + guild.Id + ".json";
|
|
||||||
if (File.Exists(path)) {
|
|
||||||
return await File.ReadAllTextAsync(path);
|
|
||||||
} else { // Write default configuration to new file
|
|
||||||
string fileContents;
|
|
||||||
using (var resStream = Assembly.GetExecutingAssembly()
|
|
||||||
.GetManifestResourceStream($"{nameof(RegexBot)}.DefaultGuildConfig.json")!) {
|
|
||||||
using var readin = new StreamReader(resStream, encoding: System.Text.Encoding.UTF8);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue