Reorganized project
Moved modules into the assembly itself to simplify development of further features and reduce complexity in building this project. Additionally, many small adjustments were made, including: - Add documentation to most public methods that had it missing - Minor style updates - Updated readme to reflect near-completion of this rewrite - Remove any last remaining references to old project name Kerobot - Update dependencies
This commit is contained in:
parent
53e0301edd
commit
1149f2800d
73 changed files with 333 additions and 313 deletions
4
.vscode/launch.json
vendored
4
.vscode/launch.json
vendored
|
@ -10,9 +10,9 @@
|
|||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
// If you have changed target frameworks, make sure to update the program path.
|
||||
"program": "${workspaceFolder}/output/Debug/net6.0/RegexBot.dll",
|
||||
"program": "${workspaceFolder}/bin/Debug/net6.0/RegexBot.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}/RegexBot",
|
||||
"cwd": "${workspaceFolder}",
|
||||
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
|
||||
"console": "internalConsole",
|
||||
"stopAtEntry": false
|
||||
|
|
6
.vscode/tasks.json
vendored
6
.vscode/tasks.json
vendored
|
@ -7,7 +7,7 @@
|
|||
"type": "process",
|
||||
"args": [
|
||||
"build",
|
||||
"${workspaceFolder}/RegexBot.sln",
|
||||
"${workspaceFolder}/RegexBot.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
|
@ -19,7 +19,7 @@
|
|||
"type": "process",
|
||||
"args": [
|
||||
"publish",
|
||||
"${workspaceFolder}/RegexBot.sln",
|
||||
"${workspaceFolder}/RegexBot.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
|
@ -33,7 +33,7 @@
|
|||
"watch",
|
||||
"run",
|
||||
"--project",
|
||||
"${workspaceFolder}/RegexBot.sln"
|
||||
"${workspaceFolder}/RegexBot.csproj"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
}
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
using Discord.WebSocket;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Collections;
|
||||
using System.Collections;
|
||||
|
||||
namespace RegexBot.Common;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a commonly-used configuration structure: an array of strings consisting of <see cref="EntityName"/> values.
|
||||
/// </summary>
|
||||
|
@ -64,6 +61,7 @@ public class EntityList : IEnumerable<EntityName> {
|
|||
/// Checks if the parameters of the given <see cref="SocketMessage"/> matches with
|
||||
/// any entity specified in this list.
|
||||
/// </summary>
|
||||
/// <param name="msg">The incoming message object with which to scan for a match.</param>
|
||||
/// <param name="keepId">
|
||||
/// Specifies if EntityName instances within this list should have their internal ID value
|
||||
/// updated if found during the matching process.
|
||||
|
@ -122,4 +120,4 @@ public class EntityList : IEnumerable<EntityName> {
|
|||
public IEnumerator<EntityName> GetEnumerator() => _innerList.GetEnumerator();
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
}
|
||||
}
|
|
@ -1,7 +1,4 @@
|
|||
using Discord.WebSocket;
|
||||
|
||||
namespace RegexBot.Common;
|
||||
|
||||
namespace RegexBot.Common;
|
||||
/// <summary>
|
||||
/// Helper class that holds an entity's name, ID, or both.
|
||||
/// Meant to be used during configuration processing in cases where the configuration expects
|
20
Common/EntityType.cs
Normal file
20
Common/EntityType.cs
Normal file
|
@ -0,0 +1,20 @@
|
|||
namespace RegexBot.Common;
|
||||
/// <summary>
|
||||
/// The type of entity specified in an <see cref="EntityName"/>.
|
||||
/// </summary>
|
||||
public enum EntityType {
|
||||
/// <summary>Default value. Is never referenced in regular usage.</summary>
|
||||
Unspecified,
|
||||
/// <summary>
|
||||
/// Userd when the <see cref="EntityName"/> represents a role.
|
||||
/// </summary>
|
||||
Role,
|
||||
/// <summary>
|
||||
/// Used when the <see cref="EntityName"/> represents a channel.
|
||||
/// </summary>
|
||||
Channel,
|
||||
/// <summary>
|
||||
/// Used when the <see cref="EntityName"/> represents a user.
|
||||
/// </summary>
|
||||
User
|
||||
}
|
|
@ -1,16 +1,37 @@
|
|||
using Discord.WebSocket;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace RegexBot.Common;
|
||||
|
||||
namespace RegexBot.Common;
|
||||
/// <summary>
|
||||
/// Represents commonly-used configuration regarding whitelist/blacklist filtering, including exemptions.
|
||||
/// </summary>
|
||||
public class FilterList {
|
||||
public enum FilterMode { None, Whitelist, Blacklist }
|
||||
/// <summary>
|
||||
/// The mode at which the <see cref="FilterList"/>'s filter criteria is operating.
|
||||
/// </summary>
|
||||
public enum FilterMode {
|
||||
/// <summary>
|
||||
/// A <see cref="FilterList"/> setting which does no filtering on the list.
|
||||
/// </summary>
|
||||
None,
|
||||
/// <summary>
|
||||
/// A <see cref="FilterList"/> setting which excludes only entites not in the list, excluding those exempted.
|
||||
/// </summary>
|
||||
Whitelist,
|
||||
/// <summary>
|
||||
/// A <see cref="FilterList"/> setting which allows all entities except those in the list, but allowing those exempted.
|
||||
/// </summary>
|
||||
Blacklist
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the mode at which the <see cref="FilterList"/>'s filter criteria is operating.
|
||||
/// </summary>
|
||||
public FilterMode Mode { get; }
|
||||
/// <summary>
|
||||
/// Gets the inner list that this instance is using for its filtering criteria.
|
||||
/// </summary>
|
||||
public EntityList FilteredList { get; }
|
||||
/// <summary>
|
||||
/// Gets the list of entities that may override filtering rules for this instance.
|
||||
/// </summary>
|
||||
public EntityList FilterExemptions { get; }
|
||||
|
||||
/// <summary>
|
||||
|
@ -70,6 +91,9 @@ public class FilterList {
|
|||
/// Determines if the parameters of the given message match up against the filtering
|
||||
/// rules described within this instance.
|
||||
/// </summary>
|
||||
/// <param name="msg">
|
||||
/// The incoming message to be checked.
|
||||
/// </param>
|
||||
/// <param name="keepId">
|
||||
/// See equivalent documentation for <see cref="EntityList.IsListMatch(SocketMessage, bool)"/>.
|
||||
/// </param>
|
10
Common/Messages.cs
Normal file
10
Common/Messages.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace RegexBot.Common;
|
||||
/// <summary>
|
||||
/// Commonly used strings used throughout the bot and modules.
|
||||
/// </summary>
|
||||
public static class Messages {
|
||||
/// <summary>
|
||||
/// Gets a string generally appropriate to display in the event of a 403 error.
|
||||
/// </summary>
|
||||
public const string ForbiddenGenericError = "Failed to perform the action due to a permissions issue.";
|
||||
}
|
|
@ -1,6 +1,4 @@
|
|||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
|
@ -9,9 +7,24 @@ namespace RegexBot.Common;
|
|||
/// Miscellaneous utility methods useful for the bot and modules.
|
||||
/// </summary>
|
||||
public static class Utilities {
|
||||
/// <summary>
|
||||
/// Gets a compiled regex that matches a channel tag and pulls its snowflake value.
|
||||
/// </summary>
|
||||
public static Regex ChannelMention { get; } = new(@"<#(?<snowflake>\d+)>", RegexOptions.Compiled);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a compiled regex that matches a custom emoji and pulls its name and ID.
|
||||
/// </summary>
|
||||
public static Regex CustomEmoji { get; } = new(@"<:(?<name>[A-Za-z0-9_]{2,}):(?<ID>\d+)>", RegexOptions.Compiled);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a compiled regex that matches a fully formed Discord handle, extracting the name and discriminator.
|
||||
/// </summary>
|
||||
public static Regex DiscriminatorSearch { get; } = new(@"(.+)#(\d{4}(?!\d))", RegexOptions.Compiled);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a compiled regex that matches a user tag and pulls its snowflake value.
|
||||
/// </summary>
|
||||
public static Regex UserMention { get; } = new(@"<@!?(?<snowflake>\d+)>", RegexOptions.Compiled);
|
||||
|
||||
/// <summary>
|
|
@ -1,7 +1,9 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace RegexBot.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a database connection using the settings defined in the bot's global configuration.
|
||||
/// </summary>
|
||||
public class BotDatabaseContext : DbContext {
|
||||
private static string? _npgsqlConnectionString;
|
||||
internal static string PostgresConnectionString {
|
||||
|
@ -17,21 +19,31 @@ public class BotDatabaseContext : DbContext {
|
|||
set => _npgsqlConnectionString ??= value;
|
||||
}
|
||||
|
||||
public DbSet<GuildLogLine> GuildLog { get; set; } = null!;
|
||||
/// <summary>
|
||||
/// Retrieves the <seealso cref="CachedUser">user cache</seealso>.
|
||||
/// </summary>
|
||||
public DbSet<CachedUser> UserCache { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the <seealso cref="CachedGuildUser">guild user cache</seealso>.
|
||||
/// </summary>
|
||||
public DbSet<CachedGuildUser> GuildUserCache { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the <seealso cref="CachedGuildMessage">guild message cache</seealso>.
|
||||
/// </summary>
|
||||
public DbSet<CachedGuildMessage> GuildMessageCache { get; set; } = null!;
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
/// <inheritdoc />
|
||||
protected sealed override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
=> optionsBuilder
|
||||
.UseNpgsql(PostgresConnectionString)
|
||||
.UseSnakeCaseNamingConvention();
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder) {
|
||||
modelBuilder.Entity<GuildLogLine>(entity => entity.Property(e => e.Timestamp).HasDefaultValueSql("now()"));
|
||||
modelBuilder.Entity<CachedUser>(entity => entity.Property(e => e.Discriminator).HasMaxLength(4).IsFixedLength());
|
||||
modelBuilder.Entity<CachedGuildUser>(entity => {
|
||||
entity.Navigation(e => e.User).AutoInclude();
|
||||
entity.HasKey(e => new { e.UserId, e.GuildId });
|
||||
entity.Property(e => e.FirstSeenTime).HasDefaultValueSql("now()");
|
||||
});
|
62
Data/CachedGuildMessage.cs
Normal file
62
Data/CachedGuildMessage.cs
Normal file
|
@ -0,0 +1,62 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace RegexBot.Data;
|
||||
/// <summary>
|
||||
/// Represents an item in the guild message cache.
|
||||
/// </summary>
|
||||
[Table("cache_guildmessages")]
|
||||
public class CachedGuildMessage {
|
||||
/// <summary>
|
||||
/// Gets the message's snowflake ID.
|
||||
/// </summary>
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.None)]
|
||||
public long MessageId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the message author's snowflake ID.
|
||||
/// </summary>
|
||||
public long AuthorId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the associated guild's snowflake ID.
|
||||
/// </summary>
|
||||
public long GuildId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the corresponding channel's snowflake ID.
|
||||
/// </summary>
|
||||
public long ChannelId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the timestamp showing when this message was originally created.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Though it's possible to draw this from <see cref="MessageId"/>, it is stored in the database
|
||||
/// as a separate field for any possible necessary use via database queries.
|
||||
/// </remarks>
|
||||
public DateTimeOffset CreatedAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the timestamp, if any, showing when this message was last edited.
|
||||
/// </summary>
|
||||
public DateTimeOffset? EditedAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of file names thata were attached to this message.
|
||||
/// </summary>
|
||||
public List<string> AttachmentNames { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets this message's content.
|
||||
/// </summary>
|
||||
public string Content { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// If included in the query, references the associated <seealso cref="CachedUser"/> for this entry.
|
||||
/// </summary>
|
||||
[ForeignKey(nameof(AuthorId))]
|
||||
[InverseProperty(nameof(CachedUser.GuildMessages))]
|
||||
public CachedUser Author { get; set; } = null!;
|
||||
}
|
36
Data/CachedGuildUser.cs
Normal file
36
Data/CachedGuildUser.cs
Normal file
|
@ -0,0 +1,36 @@
|
|||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace RegexBot.Data;
|
||||
/// <summary>
|
||||
/// Represents an item in the guild user cache.
|
||||
/// </summary>
|
||||
[Table("cache_usersinguild")]
|
||||
public class CachedGuildUser {
|
||||
/// <inheritdoc cref="CachedUser.UserId"/>
|
||||
public long UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the associated guild's snowflake ID.
|
||||
/// </summary>
|
||||
public long GuildId { get; set; }
|
||||
|
||||
/// <inheritdoc cref="CachedUser.ULastUpdateTime"/>
|
||||
public DateTimeOffset GULastUpdateTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the timestamp showing when this cache entry was first added into the database.
|
||||
/// </summary>
|
||||
public DateTimeOffset FirstSeenTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user's cached nickname in the guild.
|
||||
/// </summary>
|
||||
public string? Nickname { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If included in the query, references the associated <seealso cref="CachedUser"/> for this entry.
|
||||
/// </summary>
|
||||
[ForeignKey(nameof(UserId))]
|
||||
[InverseProperty(nameof(CachedUser.Guilds))]
|
||||
public CachedUser User { get; set; } = null!;
|
||||
}
|
48
Data/CachedUser.cs
Normal file
48
Data/CachedUser.cs
Normal file
|
@ -0,0 +1,48 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace RegexBot.Data;
|
||||
/// <summary>
|
||||
/// Represents an item in the user cache.
|
||||
/// </summary>
|
||||
[Table("cache_users")]
|
||||
public class CachedUser {
|
||||
/// <summary>
|
||||
/// Gets the user's snowflake ID.
|
||||
/// </summary>
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.None)]
|
||||
public long UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the timestamp showing when this cache entry was last updated.
|
||||
/// </summary>
|
||||
public DateTimeOffset ULastUpdateTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user's username value, without the discriminator.
|
||||
/// </summary>
|
||||
public string Username { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user's discriminator value.
|
||||
/// </summary>
|
||||
public string Discriminator { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the avatar URL, if any, for the associated user.
|
||||
/// </summary>
|
||||
public string? AvatarUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If included in the query, gets the list of associated <seealso cref="CachedGuildUser"/> entries for this entry.
|
||||
/// </summary>
|
||||
[InverseProperty(nameof(CachedGuildUser.User))]
|
||||
public ICollection<CachedGuildUser> Guilds { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// If included in the query, gets the list of associated <seealso cref="CachedGuildMessage"/> entries for this entry.
|
||||
/// </summary>
|
||||
[InverseProperty(nameof(CachedGuildMessage.Author))]
|
||||
public ICollection<CachedGuildMessage> GuildMessages { get; set; } = null!;
|
||||
}
|
3
Data/Migrations/.editorconfig
Normal file
3
Data/Migrations/.editorconfig
Normal file
|
@ -0,0 +1,3 @@
|
|||
[*.cs]
|
||||
dotnet_analyzer_diagnostic.category-CodeQuality.severity = none
|
||||
dotnet_diagnostic.CS1591.severity = none
|
|
@ -1,5 +1,4 @@
|
|||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using RegexBot.Data;
|
||||
using System.Reflection;
|
||||
|
||||
|
@ -30,14 +29,9 @@ class InstanceConfig {
|
|||
/// <summary>
|
||||
/// Sets up instance configuration object from file and command line parameters.
|
||||
/// </summary>
|
||||
internal InstanceConfig(string[] cmdline) {
|
||||
var opts = Options.ParseOptions(cmdline);
|
||||
|
||||
var path = opts.ConfigFile;
|
||||
if (path == null) { // default: config.json in working directory
|
||||
path = Path.GetDirectoryName(Assembly.GetEntryAssembly()!.Location)
|
||||
+ "." + Path.DirectorySeparatorChar + "config.json";
|
||||
}
|
||||
internal InstanceConfig() {
|
||||
var path = Path.GetDirectoryName(Assembly.GetEntryAssembly()!.Location)
|
||||
+ "." + Path.DirectorySeparatorChar + "config.json";
|
||||
|
||||
JObject conf;
|
||||
try {
|
|
@ -1,7 +1,8 @@
|
|||
namespace RegexBot;
|
||||
|
||||
/// <summary>
|
||||
/// Represents errors that occur when a module attempts to create a new guild state object.
|
||||
/// Represents an error occurring when a module attempts to create a new guild state object
|
||||
/// (that is, read or refresh its configuration).
|
||||
/// </summary>
|
||||
public class ModuleLoadException : Exception {
|
||||
/// <summary>
|
|
@ -3,7 +3,6 @@ using System.Reflection;
|
|||
using System.Text;
|
||||
|
||||
namespace RegexBot;
|
||||
|
||||
static class ModuleLoader {
|
||||
/// <summary>
|
||||
/// Given the instance configuration, loads all appropriate types from file specified in it.
|
||||
|
@ -36,21 +35,21 @@ static class ModuleLoader {
|
|||
return modules.AsReadOnly();
|
||||
}
|
||||
|
||||
static IEnumerable<RegexbotModule> LoadModulesFromAssembly(Assembly asm, RegexbotClient k) {
|
||||
static IEnumerable<RegexbotModule> LoadModulesFromAssembly(Assembly asm, RegexbotClient rb) {
|
||||
var eligibleTypes = from type in asm.GetTypes()
|
||||
where !type.IsAssignableFrom(typeof(RegexbotModule))
|
||||
where type.GetCustomAttribute<RegexbotModuleAttribute>() != null
|
||||
select type;
|
||||
k._svcLogging.DoLog(false, nameof(ModuleLoader), $"Scanning {asm.GetName().Name}");
|
||||
rb._svcLogging.DoLog(false, nameof(ModuleLoader), $"Scanning {asm.GetName().Name}");
|
||||
|
||||
var newreport = new StringBuilder("---> Found module(s):");
|
||||
var newmods = new List<RegexbotModule>();
|
||||
foreach (var t in eligibleTypes) {
|
||||
var mod = Activator.CreateInstance(t, k)!;
|
||||
var mod = Activator.CreateInstance(t, rb)!;
|
||||
newreport.Append($" {t.Name}");
|
||||
newmods.Add((RegexbotModule)mod);
|
||||
}
|
||||
k._svcLogging.DoLog(false, nameof(ModuleLoader), newreport.ToString());
|
||||
rb._svcLogging.DoLog(false, nameof(ModuleLoader), newreport.ToString());
|
||||
return newmods;
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
using RegexBot.Data;
|
||||
using RegexBot.Common;
|
||||
using RegexBot.Data;
|
||||
|
||||
namespace RegexBot.Modules.ModCommands.Commands;
|
||||
// Ban and kick commands are highly similar in implementation, and thus are handled in a single class.
|
||||
|
@ -132,7 +133,7 @@ abstract class BanKick : CommandConfig {
|
|||
await ContinueInvoke(g, msg, reason, targetId, targetQuery, targetUser);
|
||||
} catch (Discord.Net.HttpException ex) {
|
||||
if (ex.HttpCode == System.Net.HttpStatusCode.Forbidden) {
|
||||
await msg.Channel.SendMessageAsync(":x: " + Strings.ForbiddenGenericError);
|
||||
await msg.Channel.SendMessageAsync(":x: " + Messages.ForbiddenGenericError);
|
||||
} else if (ex.HttpCode == System.Net.HttpStatusCode.NotFound) {
|
||||
await msg.Channel.SendMessageAsync(":x: Encountered a 404 error when processing the request.");
|
||||
}
|
|
@ -1,4 +1,6 @@
|
|||
namespace RegexBot.Modules.ModCommands.Commands;
|
||||
using RegexBot.Common;
|
||||
|
||||
namespace RegexBot.Modules.ModCommands.Commands;
|
||||
class Unban : CommandConfig {
|
||||
private readonly string _usage;
|
||||
|
||||
|
@ -43,7 +45,7 @@ class Unban : CommandConfig {
|
|||
} catch (Discord.Net.HttpException ex) {
|
||||
const string FailPrefix = ":x: **Could not unban:** ";
|
||||
if (ex.HttpCode == System.Net.HttpStatusCode.Forbidden)
|
||||
await msg.Channel.SendMessageAsync(FailPrefix + Strings.ForbiddenGenericError);
|
||||
await msg.Channel.SendMessageAsync(FailPrefix + Messages.ForbiddenGenericError);
|
||||
else if (ex.HttpCode == System.Net.HttpStatusCode.NotFound)
|
||||
await msg.Channel.SendMessageAsync(FailPrefix + "The specified user does not exist or is not in the ban list.");
|
||||
else throw;
|
|
@ -67,7 +67,7 @@ class ResponseExecutor {
|
|||
var result = await runLine(param);
|
||||
_reports.Add((cmd, result));
|
||||
} catch (Discord.Net.HttpException ex) when (ex.HttpCode == System.Net.HttpStatusCode.Forbidden) {
|
||||
_reports.Add((cmd, FromError(Strings.ForbiddenGenericError)));
|
||||
_reports.Add((cmd, FromError(Messages.ForbiddenGenericError)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -141,7 +141,7 @@ class ResponseExecutor {
|
|||
result = await _bot.KickAsync(_guild, $"Rule '{_rule.Label}'", _user.Id,
|
||||
parameter, _rule.NotifyUserOfRemoval);
|
||||
}
|
||||
if (result.ErrorForbidden) return FromError(Strings.ForbiddenGenericError);
|
||||
if (result.ErrorForbidden) return FromError(Messages.ForbiddenGenericError);
|
||||
if (result.ErrorNotFound) return FromError("The target user is no longer in the server.");
|
||||
if (_rule.NotifyChannelOfRemoval) await _msg.Channel.SendMessageAsync(result.GetResultString(_bot));
|
||||
return FromSuccess(result.MessageSendSuccess ? null : "Unable to send notification DM.");
|
|
@ -1,5 +1,6 @@
|
|||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
global using Discord.WebSocket;
|
||||
global using Newtonsoft.Json.Linq;
|
||||
using Discord;
|
||||
|
||||
namespace RegexBot;
|
||||
class Program {
|
||||
|
@ -10,13 +11,13 @@ class Program {
|
|||
|
||||
static RegexbotClient _main = null!;
|
||||
|
||||
static async Task Main(string[] args) {
|
||||
static async Task Main() {
|
||||
StartTime = DateTimeOffset.UtcNow;
|
||||
Console.WriteLine("Bot start time: " + StartTime.ToString("u"));
|
||||
|
||||
InstanceConfig cfg;
|
||||
try {
|
||||
cfg = new InstanceConfig(args); // Program may exit within here.
|
||||
cfg = new InstanceConfig(); // Program may exit within here.
|
||||
} catch (Exception ex) {
|
||||
Console.WriteLine(ex.Message);
|
||||
Environment.ExitCode = 1;
|
||||
|
@ -33,7 +34,6 @@ class Program {
|
|||
AlwaysDownloadUsers = true
|
||||
});
|
||||
|
||||
// Kerobot class initialization - will set up services and modules
|
||||
_main = new RegexbotClient(cfg, client);
|
||||
|
||||
// Set up application close handler
|
32
Readme.md
32
Readme.md
|
@ -1,26 +1,14 @@
|
|||
# RegexBot
|
||||
**This branch is still a major work in progress, and is highly incomplete. See the legacy branch for the current working version.**
|
||||
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/J3J65TW2E)
|
||||
|
||||
RegexBot is a Discord moderation bot framework of sorts, inspired by the terrible state of Discord moderation tools a few years ago
|
||||
combined with my tendency to overengineer things until they into pseudo-libraries of their own right.
|
||||
|
||||
### Features:
|
||||
* Provides a sort of in-between interface to Discord.Net that allows modules to be written for it, its benefits being:
|
||||
* Putting together disparate bot features under a common interface.
|
||||
* Reducing duplicate code potentially leading to an inconsistent user experience.
|
||||
* Versatile JSON-based configuration.
|
||||
* High detail logging and record-keeping prevents gaps in moderation that might occur with large public bots.
|
||||
This bot includes a number of features which assist in handling the tedious details in a busy server with the goal of minimizing
|
||||
the occurrence of hidden details, arbitrary restrictions, or annoyingly unmodifiable behavior. Its configuration allows for a very high
|
||||
level of flexibility, ensuring that the bot behaves in accordance to the exact needs of your server without compromise.
|
||||
|
||||
This repository also contains...
|
||||
|
||||
# RegexBot-Modules
|
||||
An optional set of features to add to RegexBot, some of them inspired by Reddit's Automoderator.
|
||||
|
||||
This module provides a number of features to assist in watching over the tedious details in a busy server with no hidden details,
|
||||
arbitrary restrictions, or unmodifiable behavior. Its configuration allows for a very high level of flexibility, ensuring that the bot
|
||||
behaves in accordance to the exact needs of your server.
|
||||
|
||||
### Features:
|
||||
### Features
|
||||
* Create rules based on regular expression patterns
|
||||
* Follow up with custom responses ranging from sending a DM to disciplinary action
|
||||
* Create pattern-based triggers to provide information and fun to your users
|
||||
|
@ -29,6 +17,14 @@ behaves in accordance to the exact needs of your server.
|
|||
* Make things interesting by setting triggers that only activate at random
|
||||
* Individual rules and triggers can be whitelisted or blacklisted per-user, per-channel, or per-role
|
||||
* Exemptions to these filters can be applied for additional flexibility
|
||||
* High detail logging and record-keeping prevents gaps in moderation that might occur with large public bots.
|
||||
|
||||
## Documentation
|
||||
### Modules
|
||||
As mentioned above, this bot also serves as a framework of sorts, allowing others to write their own modules and expand
|
||||
the bot's feature set ever further. Its benefits are:
|
||||
* Putting together disparate bot features under a common, consistent interface.
|
||||
* Reducing duplicate code potentially leading to an inconsistent user experience.
|
||||
* Versatile JSON-based configuration.
|
||||
|
||||
## User documentation
|
||||
Coming soon?
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
global using Discord.WebSocket;
|
||||
global using Newtonsoft.Json.Linq;
|
|
@ -1,19 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<RootNamespace>RegexBot.Modules</RootNamespace>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<Authors>NoiTheCat</Authors>
|
||||
<Description>A set of standard modules for use with RegexBot.</Description>
|
||||
<BaseOutputPath>$(SolutionDir)\output</BaseOutputPath>
|
||||
<UseCommonOutputDirectory>true</UseCommonOutputDirectory>
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\RegexBot\RegexBot.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -6,10 +6,9 @@
|
|||
<Authors>NoiTheCat</Authors>
|
||||
<Description>Advanced and flexible Discord moderation bot.</Description>
|
||||
<Version>0.0.1</Version>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||
<BaseOutputPath>$(SolutionDir)\output</BaseOutputPath>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -21,18 +20,17 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommandLineParser" Version="2.9.1" />
|
||||
<PackageReference Include="Discord.Net" Version="3.7.0" />
|
||||
<PackageReference Include="Discord.Net" Version="3.7.2" />
|
||||
<PackageReference Include="EFCore.NamingConventions" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.5" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.5">
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.7" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.7">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.5" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.7" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="Npgsql" Version="6.0.4" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.4" />
|
||||
<PackageReference Include="Npgsql" Version="6.0.5" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.5" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL.Design" Version="1.1.0" />
|
||||
</ItemGroup>
|
||||
|
28
RegexBot.sln
28
RegexBot.sln
|
@ -1,28 +0,0 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.30114.105
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RegexBot", "RegexBot\RegexBot.csproj", "{F7CDACE1-C74E-451E-A9C2-ED717BF72C1C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RegexBot-Modules", "RegexBot-Modules\RegexBot-Modules.csproj", "{347A1912-4F7D-419B-A159-6671791568CC}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{F7CDACE1-C74E-451E-A9C2-ED717BF72C1C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F7CDACE1-C74E-451E-A9C2-ED717BF72C1C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F7CDACE1-C74E-451E-A9C2-ED717BF72C1C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F7CDACE1-C74E-451E-A9C2-ED717BF72C1C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{347A1912-4F7D-419B-A159-6671791568CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{347A1912-4F7D-419B-A159-6671791568CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{347A1912-4F7D-419B-A159-6671791568CC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{347A1912-4F7D-419B-A159-6671791568CC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -1,12 +0,0 @@
|
|||
namespace RegexBot.Common;
|
||||
|
||||
/// <summary>
|
||||
/// The type of entity specified in an <see cref="EntityName"/>.
|
||||
/// </summary>
|
||||
public enum EntityType {
|
||||
/// <summary>Default value. Is never referenced in regular usage.</summary>
|
||||
Unspecified,
|
||||
Role,
|
||||
Channel,
|
||||
User
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
/// <summary>
|
||||
/// Commonly used strings used throughout the program and modules.
|
||||
/// </summary>
|
||||
public static class Strings {
|
||||
public const string ForbiddenGenericError = "Failed to perform the action due to a permissions issue.";
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace RegexBot.Data;
|
||||
[Table("cache_messages")]
|
||||
public class CachedGuildMessage {
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.None)]
|
||||
public long MessageId { get; set; }
|
||||
|
||||
public long AuthorId { get; set; }
|
||||
|
||||
public long GuildId { get; set; }
|
||||
|
||||
public long ChannelId { get; set; }
|
||||
|
||||
public DateTimeOffset CreatedAt { get; set; }
|
||||
|
||||
public DateTimeOffset? EditedAt { get; set; }
|
||||
|
||||
public List<string> AttachmentNames { get; set; } = null!;
|
||||
|
||||
public string Content { get; set; } = null!;
|
||||
|
||||
[ForeignKey(nameof(AuthorId))]
|
||||
[InverseProperty(nameof(CachedUser.GuildMessages))]
|
||||
public CachedUser Author { get; set; } = null!;
|
||||
|
||||
internal new CachedGuildMessage MemberwiseClone() => (CachedGuildMessage)base.MemberwiseClone();
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace RegexBot.Data;
|
||||
|
||||
[Table("cache_userguild")]
|
||||
public class CachedGuildUser {
|
||||
public long UserId { get; set; }
|
||||
public long GuildId { get; set; }
|
||||
public DateTimeOffset GULastUpdateTime { get; set; }
|
||||
public DateTimeOffset FirstSeenTime { get; set; }
|
||||
public string? Nickname { get; set; }
|
||||
|
||||
[ForeignKey(nameof(UserId))]
|
||||
[InverseProperty(nameof(CachedUser.Guilds))]
|
||||
public CachedUser User { get; set; } = null!;
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace RegexBot.Data;
|
||||
|
||||
[Table("cache_user")]
|
||||
public class CachedUser {
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.None)]
|
||||
public long UserId { get; set; }
|
||||
public DateTimeOffset ULastUpdateTime { get; set; }
|
||||
public string Username { get; set; } = null!;
|
||||
public string Discriminator { get; set; } = null!;
|
||||
public string? AvatarUrl { get; set; }
|
||||
|
||||
[InverseProperty(nameof(CachedGuildUser.User))]
|
||||
public ICollection<CachedGuildUser> Guilds { get; set; } = null!;
|
||||
|
||||
[InverseProperty(nameof(CachedGuildMessage.Author))]
|
||||
public ICollection<CachedGuildMessage> GuildMessages { get; set; } = null!;
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace RegexBot.Data;
|
||||
|
||||
[Table("guild_log")]
|
||||
[Index(nameof(GuildId))]
|
||||
public class GuildLogLine {
|
||||
public int Id { get; set; }
|
||||
public long GuildId { get; set; }
|
||||
public DateTimeOffset Timestamp { get; set; }
|
||||
public string Source { get; set; } = null!;
|
||||
public string Message { get; set; } = null!;
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
using CommandLine;
|
||||
using CommandLine.Text;
|
||||
|
||||
namespace RegexBot;
|
||||
|
||||
/// <summary>
|
||||
/// Command line options
|
||||
/// </summary>
|
||||
class Options {
|
||||
[Option('c', "config", Default = null,
|
||||
HelpText = "Custom path to instance configuration. Defaults to config.json in bot directory.")]
|
||||
public string ConfigFile { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Command line arguments parsed here. Depending on inputs, the program can exit here.
|
||||
/// </summary>
|
||||
public static Options ParseOptions(string[] args) {
|
||||
// Parser will not write out to console by itself
|
||||
var parser = new Parser(config => config.HelpWriter = null);
|
||||
Options? opts = null;
|
||||
|
||||
var result = parser.ParseArguments<Options>(args);
|
||||
result.WithParsed(p => opts = p);
|
||||
result.WithNotParsed(p => {
|
||||
// Taking some extra steps to modify the header to make it resemble our welcome message.
|
||||
var ht = HelpText.AutoBuild(result);
|
||||
ht.Heading += " - https://github.com/NoiTheCat/RegexBot";
|
||||
Console.WriteLine(ht.ToString());
|
||||
Environment.Exit(1);
|
||||
});
|
||||
return opts!;
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
namespace RegexBot;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies to the Kerobot module loader that the target class should be treated as a module instance.
|
||||
/// When the program scans an assembly which has been specified in its instance configuration to be loaded,
|
||||
/// the program searches for classes implementing <see cref="RegexbotModule"/> that also contain this attribute.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||
public class RegexbotModuleAttribute : Attribute { }
|
|
@ -1,8 +1,6 @@
|
|||
using Discord.WebSocket;
|
||||
using System.Reflection;
|
||||
using System.Reflection;
|
||||
|
||||
namespace RegexBot;
|
||||
|
||||
/// <summary>
|
||||
/// The RegexBot client instance.
|
||||
/// </summary>
|
|
@ -1,6 +1,4 @@
|
|||
using Discord.WebSocket;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using RegexBot.Common;
|
||||
using RegexBot.Common;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace RegexBot;
|
8
RegexbotModuleAttribute.cs
Normal file
8
RegexbotModuleAttribute.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace RegexBot;
|
||||
|
||||
/// <summary>
|
||||
/// Provides a hint to the module loader that the class it is applied to should be treated as a module instance.
|
||||
/// When the program scans an assembly, it is scanned for classes which implement <see cref="RegexbotModule"/> and have this attribute.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||
public class RegexbotModuleAttribute : Attribute { }
|
|
@ -1,4 +1,5 @@
|
|||
using Discord.Net;
|
||||
using RegexBot.Common;
|
||||
|
||||
// Instances of this class are created by CommonFunctionService and are meant to be sent to modules,
|
||||
// therefore we put this in the root RegexBot namespace despite being specific to this service.
|
||||
|
@ -104,7 +105,7 @@ public class BanKickResult {
|
|||
if (!MessageSendSuccess) msg += "\n(User was unable to receive notification message.)";
|
||||
} else {
|
||||
if (ErrorNotFound) msg += ": The specified user could not be found.";
|
||||
else if (ErrorForbidden) msg += ": " + Strings.ForbiddenGenericError;
|
||||
else if (ErrorForbidden) msg += ": " + Messages.ForbiddenGenericError;
|
||||
}
|
||||
|
||||
return msg;
|
|
@ -1,15 +1,10 @@
|
|||
using Discord.Net;
|
||||
using Discord.WebSocket;
|
||||
|
||||
namespace RegexBot.Services.CommonFunctions;
|
||||
|
||||
/// <summary>
|
||||
/// Implements certain common actions that modules may want to perform. Using this service to perform those
|
||||
/// functions may help enforce a sense of consistency across modules when performing common actions, and may
|
||||
/// inform services which provide any additional features the ability to respond to those actions ahead of time.
|
||||
///
|
||||
/// This is currently an experimental section. If it turns out to not be necessary, this service will be removed and
|
||||
/// modules may resume executing common actions on their own.
|
||||
/// </summary>
|
||||
internal class CommonFunctionsService : Service {
|
||||
public CommonFunctionsService(RegexbotClient bot) : base(bot) { }
|
|
@ -1,5 +1,4 @@
|
|||
using Discord.WebSocket;
|
||||
using RegexBot.Services.CommonFunctions;
|
||||
using RegexBot.Services.CommonFunctions;
|
||||
|
||||
namespace RegexBot;
|
||||
partial class RegexbotClient {
|
||||
|
@ -29,7 +28,12 @@ partial class RegexbotClient {
|
|||
/// Similar to <see cref="BanAsync(SocketGuild, string, ulong, int, string, bool)"/>, but making use of an
|
||||
/// EntityCache lookup to determine the target.
|
||||
/// </summary>
|
||||
/// <param name="targetSearch">The EntityCache search string.</param>
|
||||
/// <param name="guild">The guild in which to attempt the ban.</param>
|
||||
/// <param name="source">The user, module, or service which is requesting this action to be taken.</param>
|
||||
/// <param name="targetSearch">The user which to perform the action to (as a query to the entity cache).</param>
|
||||
/// <param name="purgeDays">Number of days of prior post history to delete on ban. Must be between 0-7.</param>
|
||||
/// <param name="reason">Reason for the action. Sent to the Audit Log and user (if specified).</param>
|
||||
/// <param name="sendDMToTarget">Specify whether to send a direct message to the target user informing them of the action.</param>
|
||||
public async Task<BanKickResult> BanAsync(SocketGuild guild,
|
||||
string source,
|
||||
string targetSearch,
|
||||
|
@ -54,10 +58,7 @@ partial class RegexbotClient {
|
|||
/// Reason for the action. Sent to the guild's audit log and, if
|
||||
/// <paramref name="sendDMToTarget"/> is <see langword="true"/>, the target.
|
||||
/// </param>
|
||||
/// <param name="sendDMToTarget">
|
||||
/// Specify whether to send a direct message to the target user informing them of the action
|
||||
/// (that is, a ban/kick message).
|
||||
/// </param>
|
||||
/// <param name="sendDMToTarget">Specify whether to send a direct message to the target user informing them of the action.</param>
|
||||
public Task<BanKickResult> KickAsync(SocketGuild guild,
|
||||
string source,
|
||||
ulong targetUser,
|
||||
|
@ -69,7 +70,14 @@ partial class RegexbotClient {
|
|||
/// Similar to <see cref="KickAsync(SocketGuild, string, ulong, string, bool)"/>, but making use of an
|
||||
/// EntityCache lookup to determine the target.
|
||||
/// </summary>
|
||||
/// <param name="targetSearch">The EntityCache search string.</param>
|
||||
/// <param name="guild">The guild in which to attempt the kick.</param>
|
||||
/// <param name="source">The user, module, or service which is requesting this action to be taken.</param>
|
||||
/// <param name="targetSearch">The user which to perform the action towards (processed as a query to the entity cache).</param>
|
||||
/// <param name="reason">
|
||||
/// Reason for the action. Sent to the guild's audit log and, if
|
||||
/// <paramref name="sendDMToTarget"/> is <see langword="true"/>, the target.
|
||||
/// </param>
|
||||
/// <param name="sendDMToTarget">Specify whether to send a direct message to the target user informing them of the action.</param>
|
||||
public async Task<BanKickResult> KickAsync(SocketGuild guild,
|
||||
string source,
|
||||
string targetSearch,
|
|
@ -1,9 +1,10 @@
|
|||
// Despite specific to CommonFunctionsService, this enum is meant to be visible by modules too,
|
||||
// thus it is placed within the root namespace.
|
||||
namespace RegexBot;
|
||||
namespace RegexBot;
|
||||
/// <summary>
|
||||
/// Specifies possible outcomes for the removal of a user from a guild.
|
||||
/// </summary>
|
||||
// Despite specific to CommonFunctionsService, this enum is meant to be visible by modules too,
|
||||
// thus it is placed within the root namespace.
|
||||
// TODO Tends to be unused except internally. Look into removing.
|
||||
public enum RemovalType {
|
||||
/// <summary>
|
||||
/// Default value. Not used in any actual circumstances.
|
|
@ -2,9 +2,7 @@
|
|||
|
||||
namespace RegexBot.Services.EntityCache;
|
||||
/// <summary>
|
||||
/// Provides and maintains a database-backed cache of entities. Portions of information collected by this
|
||||
/// service may be used by modules, while other portions are useful only for external applications which may
|
||||
/// require this information, such as an external web interface.
|
||||
/// Provides and maintains a database-backed cache of entities.
|
||||
/// </summary>
|
||||
class EntityCacheService : Service {
|
||||
private readonly UserCachingSubservice _uc;
|
|
@ -1,5 +1,4 @@
|
|||
using Discord.WebSocket;
|
||||
using RegexBot.Data;
|
||||
using RegexBot.Data;
|
||||
using RegexBot.Services.EntityCache;
|
||||
|
||||
namespace RegexBot;
|
|
@ -1,5 +1,4 @@
|
|||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
using RegexBot.Data;
|
||||
using static RegexBot.RegexbotClient;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
using Discord.WebSocket;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using RegexBot.Common;
|
||||
using RegexBot.Data;
|
||||
|
||||
|
@ -101,8 +101,7 @@ class UserCachingSubservice {
|
|||
internal CachedGuildUser? DoGuildUserQuery(ulong guildId, string search) {
|
||||
static CachedGuildUser? innerQuery(ulong guildId, ulong? sID, (string name, string? disc)? nameSearch) {
|
||||
var db = new BotDatabaseContext();
|
||||
|
||||
var query = db.GuildUserCache.Where(c => c.GuildId == (long)guildId);
|
||||
var query = db.GuildUserCache.Include(gu => gu.User).Where(c => c.GuildId == (long)guildId);
|
||||
if (sID.HasValue)
|
||||
query = query.Where(c => c.UserId == (long)sID.Value);
|
||||
if (nameSearch != null) {
|
|
@ -1,7 +1,6 @@
|
|||
using RegexBot.Services.Logging;
|
||||
|
||||
namespace RegexBot;
|
||||
|
||||
partial class RegexbotClient {
|
||||
// Access set to internal for ModuleBase and Service base class
|
||||
internal readonly LoggingService _svcLogging;
|
|
@ -1,7 +1,6 @@
|
|||
using RegexBot.Services.ModuleState;
|
||||
|
||||
namespace RegexBot;
|
||||
|
||||
partial class RegexbotClient {
|
||||
// Access set to internal for ModuleBase
|
||||
internal readonly ModuleStateService _svcGuildState;
|
|
@ -1,11 +1,8 @@
|
|||
using Discord.WebSocket;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using RegexBot.Common;
|
||||
using System.Reflection;
|
||||
|
||||
namespace RegexBot.Services.ModuleState;
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
|
@ -15,8 +12,6 @@ class ModuleStateService : Service {
|
|||
private readonly Dictionary<ulong, EntityList> _moderators;
|
||||
private readonly Dictionary<ulong, Dictionary<Type, object?>> _stateData;
|
||||
|
||||
const string GuildLogSource = "Configuration loader";
|
||||
|
||||
public ModuleStateService(RegexbotClient bot) : base(bot) {
|
||||
_moderators = new();
|
||||
_stateData = new();
|
|
@ -1,5 +1,4 @@
|
|||
namespace RegexBot.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Base class for services.
|
||||
/// </summary>
|
Loading…
Reference in a new issue